import json
# Why the import is nesscary 
# Without import json, the name json isn't defined, and you'll get a NameError the 
# moment you call json.dump , etc. 

#Modular design Python keeps functionality organized in modules 
from data classes import dataclass, asdict 
from typing import List, Optional 


#-- Data Label -- 
@dataclass 
class Actor: 
    name: str 
    medium: str 
    role: str 
    start_year: int 
    end_year Optional[int] = None #None if ongoing or single year 

#-- Seed Data -- Well known Superman Portrayals 
def seed_actors() -> List[Actor]: 
    return [
        Actor(" Kirk Alyn", "Serial", "Superman", 1948, 1950), 
        Actor( "George Reeves", "TV", "Superman", 1952, 1958), 
        Actor(" Chrisopher Reeve", "Film", "Superman", 1978, 1987),
        Actor("Dean Cain", "TV", "Superman", 1993, 1997), 
        Actor("Tom Welling", "TV", "Clark Kent", 2001, 2011), 
        Actor("Brandon Routh", "Film", "Superman", 2006, 2006), 
        Actor("Henry Cavill", "Film", "Superman", 2013, 2022), 
        Actor("Tyler Hoechlin", "TV", "Superman", 2016, None), 
    ]

#Core functions 
def add_actor (actors: List[Actor], actor: Actor) -> None: 
    """Append a new to the list"""
    actors.append(actor)

def list_all(actors: List[Actor]) -> None: 
    print("\nAll Superman Actors:")
    for idx, a in enumerate(actors): 
        span = f"{a.start_year}-{a.end_year or 'present'}"
        print(f" {idx:>2}: {a.name} ({a.medium}, {a.role}, {span})")

def filter_by_medium(actors: List[Actor], medium: str) -> List[Actor]: 
    return[a for a in actors if a.medium.lower() == medium.lower()]

def filter_by_year_range(actors: List[Actor], start: int, end: int) -> [Actor]: 
    """Return actors active in any part of [start, end]."""

    result = []
    for a in actors: 
        a_end = a.end_year or 9999
        if not (a_end < start or a.start_year > end): 
            result.append(a)
        return result 

def find_by_name(actors: List[Actor], name_query: str) -> List[Actor]: 
    nq = name_query.lower()
    return[a for a in actors if nq in a.name.lower()]


#-- pop() helpers --- 
def pop_last_actor(actors: List[Actor]) -> Optional[Actor]: 
    """ Removes and returns the last actor, returns none if the list is empty"""

    if not actors: 
        return None 
    return actors.pop() #Same as actors.pop(-1)

def pop_actor_at(actors: List[Actor], index: int) -> Tuple[bool, Optional[Actor], 
Optional[str]]: 

    try: 
        removed = actors.pop(index)
        return True, removed, None 
    except IndexError: 
        return False, None, f"Index {index} is out of range (0... {len(actors) -1})."

# -- Persistence(optional) ---- 
def save_to_json(actors: List[Actor],path: str) -> None: 
    payload = [asdict(a) for a in actors]
    with open(path, "w", encoding= "utf-8") as f: 
        json.dump(payload, f, indent=2, ensure_ascii=False) 

def load_from_json(path: str) -> List[Actor]: 
    with open(path, "r", encoding="utf-8") as f: 
        raw = json.load(f)
    return [Actor(**item) for item in raw]

#Demo / CLI 

def main(): 
    actors - seed_actors()

# Show initial list 
    list_all(actors)

#Ex: Append a new actor 
    print("\nAppending a new actor entry...")
    new_actor = Actor(
        name= "David Cornswet", 
        medium="Film", 
        role="Superman"
        start_year=2025, #Adjust if needed
        end_year=None
    )
    add_actor(actors, new_actor)
    list_all(actors)

    #pop() - remove the last actor 
    print("\nPopping the last actor...")
    removed = pop_last_actor(actors)
    if removed: 
        print(f"Removed: {removed.name}")
    else: 
        print("No actor to remove (list is empty).")
    list_all(actors)

    #pop() - remove at a specific index 
    print("\nPopping actor at index 2....")

Embed on website

To embed this project on your website, copy the following code and paste it into your website's HTML: