Python Arena
Step 1 of 4

An object as an attribute

Things are often made of other things, or belong to them: a book has an author, an order has a customer, a car has an engine. A program models that with an attribute whose value is another object, made from a class of your own:

class Author:
    def __init__(self, name, country):
        self.name = name
        self.country = country


class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author


writer = Author("Ana Lind", "Sweden")
novel = Book("The Quiet Sea", writer)
poems = Book("Salt and Stone", writer)

print(novel.title)
print(novel.author.name)
print(poems.author.country)
Output
The Quiet Sea
Ana Lind
Sweden

novel.author.name takes two hops. novel.author gives the Author object stored in the book, and .name then reads that author's name. It means the same as these two lines, with a variable in between:

who = novel.author
print(who.name)

The attribute holds a reference, as every variable does. Both books refer to the same Author object, so if the author changed her name with writer.name = "Ana Berg", both books would show the new name at once.

The outer object uses the inner one

The methods of the outer object can use the inner object's attributes and call its methods. And when an f-string has an object in its braces, Python uses that object's own __str__:

class Engine:
    def __init__(self, horsepower):
        self.horsepower = horsepower
        self.running = False

    def start(self):
        self.running = True

    def __str__(self):
        state = "running" if self.running else "off"
        return f"{self.horsepower} hp, {state}"


class Car:
    def __init__(self, model, engine):
        self.model = model
        self.engine = engine

    def start(self):
        self.engine.start()
        print(f"{self.model}: vroom")

    def __str__(self):
        return f"{self.model} (engine: {self.engine})"


car = Car("Roadster", Engine(150))
print(car)
car.start()
print(car)
Output
Roadster (engine: 150 hp, off)
Roadster: vroom
Roadster (engine: 150 hp, running)

The car doesn't know how to start an engine; self.engine.start() asks the engine to do it. Each class looks after its own attributes, and the car only talks to its engine through the engine's methods. The engine object is created right in the call, Car("Roadster", Engine(150)): it doesn't need a variable of its own, because the car keeps a reference to it.

Your task

Challenge 1 of 3

Fill in the blanks so the program prints the customer's shipping label:

Lea Park
12 Elm Street
Springfield

Challenge 2

What does this program print? Type each line it prints.

Challenge 3

An Airport has a code and a city, and a Flight has a flight number and two airports, where it leaves from (origin) and where it lands (destination). Write two methods in the class Flight:

  • __str__(self) returns a string such as AY 123: Helsinki (HEL) to Oslo (OSL), using the airports' own __str__.
  • return_flight(self, number) returns a new Flight object with the given number that flies the same route the other way around.

For example, this code:

hel = Airport("HEL", "Helsinki")
osl = Airport("OSL", "Oslo")
there = Flight("AY 123", hel, osl)
back = there.return_flight("AY 124")
print(there)
print(back)

prints:

AY 123: Helsinki (HEL) to Oslo (OSL)
AY 124: Oslo (OSL) to Helsinki (HEL)