Check your understanding of the previous reading: primitive and composite types, lists and dataclasses nested inside each other, and working through heavily nested data.

Question 1

Label each of the following as a "Primitive type", "Composite type", or "Not a type":
Boolean
String
List
Attribute
Types created as dataclasses
Value
Integer
Float
None
Return
For
Literal

Question 2

Lists can be composed of instances of dataclasses.

Select one answer, then check.

  • Correct. A list can hold dataclass instances, like the houses field of Neighborhood, a list of House instances.
  • Not quite. A list can hold dataclass instances, like the houses field of Neighborhood, a list of House instances.

Question 3

Dataclass instances can have lists.

Select one answer, then check.

  • Correct. A field can hold a list, like houses: list[House] in Neighborhood.
  • Not quite. A field can hold a list, like houses: list[House] in Neighborhood.

Question 4

Strings can be composed of Lists.

Select one answer, then check.

  • Not quite. A string is a primitive type: it holds characters, not other values such as lists.
  • Correct. A string is a primitive type: it holds characters, not other values such as lists.

Question 5

What is the type of the following expression?

[[0]]

Select one answer, then check.

  • Correct. The outer list has one element, the list [0], which holds the integer 0.
  • Not quite. That would be [0]; here that list is itself the element of another list.
  • Not quite. 0 is an integer, but it sits inside two lists.
  • Not quite. A list can hold other lists, so this is a valid expression.

Question 6

How many elements are in the following list?

from dataclasses import dataclass

@dataclass
class Activity:
    name: str
    high_energy: bool

@dataclass
class Animal:
    name: str
    age: int
    hobby: Activity
    type: str

animals = [
    Animal("Ada", 4, Activity("Fetch", True), "dog"),
    Animal("Babbage", 5, Activity("Napping", True), "dog"),
    Animal("Captain", 1, Activity("Destruction", True), "cat")
]

Select one answer, then check.

  • Not quite. The list is not empty: it holds an Animal instance for each line.
  • Correct. The list holds three Animal instances. Their fields, and the nested Activity instances, are not elements of the list.
  • Not quite. An Animal has four fields, but fields are not elements of the list.
  • Not quite. That counts the four fields of each of the three animals, but fields are not elements of the list.
  • Not quite. That counts every value in the animals, including the fields of the nested Activity instances; the list itself has fewer elements.

Question 7

Given the following code:

from dataclasses import dataclass

@dataclass
class Movie:
    name: str
    length: int
    cast: list[str]

movies = [
    Movie("Jurassic Park", 127, ["Goldblum", "Neill", "Dern"]),
    Movie("Castle in the Sky", 140, ["Paquin", "Beek", "Leachman"]),
    Movie("It", 135, ["Curry", "Green", "Brandis"])
]

What is the value of the following expressions?

ExpressionValue
movies[0].name
movies[2].name
movies[0][127]
movies[1][1]
movies[127].name
movies[2].cast[0]
movies[2].cast.Curry
movies[1].cast[2]
movies[2].Curry
movies[-1].length

The next few questions will all use the same code:

from dataclasses import dataclass

@dataclass
class Profit:
    # In millions
    costs: int
    revenue: int

@dataclass
class Movie:
    name: str
    length: int
    profit: Profit
    cast: list[str]

@dataclass
class Theater:
    name: str
    is_open: bool
    showing: list[Movie]

theaters = [
    Theater("Main Street Theater", True, [
        Movie("Jurassic Park", 127, Profit(63, 1046),
            ["Goldblum", "Neill", "Dern"]),
        Movie("Castle in the Sky", 140, Profit(3, 16),
            ["Paquin", "Beek", "Leachman"]),
        Movie("The Notebook", 121, Profit(29, 118),
            ["McAdams", "Gosling", "Rowlands"])
    ]),
    Theater("BlockBuster Cinema", False, [
        Movie("Cats", 110, Profit(100, 75),
            ["Dench", "Corden", "Derulo"]),
        Movie("Morbius", 104, Profit(86, 45),
            ["Leto", "Smith", "Arjona"])
    ]),
    Theater("All Star Movies", True, [
        Movie("Gone with the Wind", 221, Profit(4, 390),
            ["Leigh", "Gable", "Havilland"])
    ])
]

A diagram is also provided to help you understand the data and its relationships.

A class diagram for the Theater, Movie, and Profit relationships

Question 8

Given the code above and the code below:

alpha = 0
for theater in theaters:
    for movie in theater.showing:
        alpha += movie.profit.costs

Inside of the loop body, what are the most accurate types of the following expressions:

ExpressionType
theater
theater.name
theater.length
theater.showing
movie
movie.name
movie.length
movie.profit
movie.profit.costs
alpha

Question 9

Given the following function:

def alpha(theaters: list[Theater]) -> str:
    for theater in theaters:
        for movie in theater.showing:
            potential = beta(movie)
            if potential < 0:
                return movie.name
    return None

def beta(movie: Movie) -> int:
    profit = movie.profit
    return profit.revenue - profit.costs

What will the expression print(alpha(theaters)) output?

Show worked solution

beta returns a movie's revenue minus its costs. alpha goes through the movies of every theater, open or not, and returns the name of the first movie where that is negative. The Main Street Theater movies all make a profit (983, 13 and 89); the first movie of BlockBuster Cinema, Cats, gives 75 - 100 = -25. So alpha returns "Cats" and print shows Cats.

Question 10

Given the following function:

def unknown(theaters: list[Theater]) -> str:
    theaters = step1(theaters)
    if not theaters:
        return "No results"
    result = step2(theaters)
    return result.name

def step1(theaters: list[Theater]) -> list[Theater]:
    result = []
    for theater in theaters:
        if theater.is_open:
            result.append(theater)
    return result

def step2(theaters: list[Theater]) -> Theater:
    result = theaters[0]
    result_length = theaters[0].showing[0].length
    for theater in theaters:
        for movie in theater.showing:
            if result_length > movie.length:
                result = theater
                result_length = movie.length
    return result

What will the expression print(unknown(theaters)) output?

Show worked solution

step1 keeps the open theaters: Main Street Theater and All Star Movies. That list is not empty, so step2 looks for the theater showing the shortest movie. It starts with Main Street Theater and the length of its first movie, 127. The Notebook (121) is shorter, so the result stays Main Street Theater with 121; Castle in the Sky (140) and Gone with the Wind (221) are longer. unknown returns that theater's name, so print shows Main Street Theater.