Python Arena
Step 2 of 4

The constructor and self

A constructor sets up each new object as it is made, so that every object gets the same attributes. In Python, the constructor is a method with the special name __init__ (two underscores on each side of init), written inside the class:

class Planet:
    def __init__(self, name, moons):
        self.name = name
        self.moons = moons


home = Planet("Earth", 1)
far = Planet("Neptune", 16)

print(home.name, home.moons)
print(far.name, far.moons)
Output
Earth 1
Neptune 16

A function defined inside a class is a method, and __init__ is the one that Python calls by itself whenever a new object is made.

self

The first parameter of __init__ is always self: the object being set up. You never pass it yourself, because Python does that. So __init__ here has three parameters, but you call Planet with two arguments.

Follow one call, Planet("Earth", 1), from start to end:

  1. Python makes a new, empty Planet object.
  2. It calls __init__. Inside the call there are three local variables, the parameters: self refers to the new object, name is "Earth" and moons is 1.
  3. self.name = name reads the local variable name and stores its value in the object, under the attribute name name. The next line does the same for moons.
  4. __init__ ends, and its local variables end with it, as in any function. What was stored in the object stays there: the object now has name "Earth" and moons 1.
  5. The finished object is the value of Planet("Earth", 1), and it is stored in home, so home.name is "Earth".

So in self.name = name, the name on the right is the parameter and the name after self. is the attribute. They only share the spelling. A constructor def __init__(self, planet_name, moons): with self.name = planet_name would make exactly the same objects, though it is more to read.

Forgetting self. is a common mistake. Then name = name only assigns the local variable to itself, and the object gets nothing:

class Planet:
    def __init__(self, name, moons):
        name = name
        self.moons = moons


home = Planet("Earth", 1)
print(home.moons)
print(home.name)
Output
1
Python stops with
Traceback (most recent call last):
  File "main.py", line 9, in <module>
    print(home.name)
          ^^^^^^^^^
AttributeError: 'Planet' object has no attribute 'name'

The right arguments

The constructor's parameters decide what you must give when you make an object. Leave one out, and Python names the missing one:

class Planet:
    def __init__(self, name, moons):
        self.name = name
        self.moons = moons


far = Planet("Neptune")
Python stops with
Traceback (most recent call last):
  File "main.py", line 7, in <module>
    far = Planet("Neptune")
TypeError: Planet.__init__() missing 1 required positional argument: 'moons'

If the error says Planet() takes no arguments instead, Python didn't find a constructor at all: check the spelling of __init__, with two underscores on each side.

As in functions, type hints are welcome in a constructor: def __init__(self, name: str, moons: int):. They don't change what the code does.

Your task

Challenge 1 of 3

Fill in the blanks, so that the constructor stores all three arguments in the object and the program prints:

Kindred by Octavia Butler (1979)

Challenge 2

This class has two mistakes. Fix them, so that the program prints:

Up 2009

Challenge 3

Write a class Flight with a constructor that takes the flight's code, where it leaves from, where it goes to and how long it takes in minutes, and stores them in the attributes code, origin, destination and minutes.

This code:

flight = Flight("PA101", "Lisbon", "Oslo", 230)
print(f"{flight.code}: {flight.origin} to {flight.destination}")
print(flight.minutes // 60, "h", flight.minutes % 60, "min")

prints

PA101: Lisbon to Oslo
3 h 50 min