{"id":"classes-constructor","slug":"constructor","title":"The constructor and self","text":"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:\n\n```python\nclass Planet:\n    def __init__(self, name, moons):\n        self.name = name\n        self.moons = moons\n\n\nhome = Planet(\"Earth\", 1)\nfar = Planet(\"Neptune\", 16)\n\nprint(home.name, home.moons)\nprint(far.name, far.moons)\n```\n\n```output\nEarth 1\nNeptune 16\n```\n\nA function defined inside a class is a method, and `__init__` is the one that Python calls by itself whenever a new object is made.\n\n### self\n\nThe 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.\n\nFollow one call, `Planet(\"Earth\", 1)`, from start to end:\n\n1. Python makes a new, empty `Planet` object.\n2. 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`.\n3. `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`.\n4. `__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`.\n5. The finished object is the value of `Planet(\"Earth\", 1)`, and it is stored in `home`, so `home.name` is `\"Earth\"`.\n\nSo 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.\n\nForgetting `self.` is a common mistake. Then `name = name` only assigns the local variable to itself, and the object gets nothing:\n\n```python\nclass Planet:\n    def __init__(self, name, moons):\n        name = name\n        self.moons = moons\n\n\nhome = Planet(\"Earth\", 1)\nprint(home.moons)\nprint(home.name)\n```\n\n```output\n1\n```\n\n```crash\nTraceback (most recent call last):\n  File \"main.py\", line 9, in <module>\n    print(home.name)\n          ^^^^^^^^^\nAttributeError: 'Planet' object has no attribute 'name'\n```\n\n### The right arguments\n\nThe constructor's parameters decide what you must give when you make an object. Leave one out, and Python names the missing one:\n\n```python\nclass Planet:\n    def __init__(self, name, moons):\n        self.name = name\n        self.moons = moons\n\n\nfar = Planet(\"Neptune\")\n```\n\n```crash\nTraceback (most recent call last):\n  File \"main.py\", line 7, in <module>\n    far = Planet(\"Neptune\")\nTypeError: Planet.__init__() missing 1 required positional argument: 'moons'\n```\n\nIf 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.\n\nAs in functions, type hints are welcome in a constructor: `def __init__(self, name: str, moons: int):`. They don't change what the code does.\n","task":"Fill in the blanks, so that the constructor stores all three arguments in the object and the program prints:\n\n```\nKindred by Octavia Butler (1979)\n```\n","kind":"fill","seed":"class Book:\n    def __init__([[self]], title, author, year):\n        self.title = title\n        [[self.author]] = author\n        self.year = [[year]]\n\n\nnovel = Book(\"Kindred\", \"Octavia Butler\", 1979)\nprint(f\"{novel.title} by {novel.author} ({novel.year})\")\n","solution":"class Book:\n    def __init__(self, title, author, year):\n        self.title = title\n        self.author = author\n        self.year = year\n\n\nnovel = Book(\"Kindred\", \"Octavia Butler\", 1979)\nprint(f\"{novel.title} by {novel.author} ({novel.year})\")\n","hints":["The first parameter of every constructor is the object itself.","Each attribute is set with `self.` in front of its name: `self.author = author`.","The year comes from the parameter `year`."],"tests":[{"name":"Output","stdin":"","expect":"Kindred by Octavia Butler (1979)","hidden":false}],"require":[],"forbid":[],"more":[{"task":"This class has two mistakes. Fix them, so that the program prints:\n\n```\nUp 2009\n```\n","kind":"code","seed":"class Movie:\n    def _init_(self, title, year):\n        self.title = title\n        year = year\n\n\nfilm = Movie(\"Up\", 2009)\nprint(film.title, film.year)\n","solution":"class Movie:\n    def __init__(self, title, year):\n        self.title = title\n        self.year = year\n\n\nfilm = Movie(\"Up\", 2009)\nprint(film.title, film.year)\n","hints":["Check it and read the error: `Movie() takes no arguments` means Python found no constructor. Count the underscores.","The constructor is `__init__`, with two underscores on each side.","Once that's fixed, the year isn't stored in the object: it needs `self.` in front."],"tests":[{"name":"Output","stdin":"","expect":"Up 2009","hidden":false}],"require":[],"forbid":[]},{"task":"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`.\n\nThis code:\n\n```python\nflight = Flight(\"PA101\", \"Lisbon\", \"Oslo\", 230)\nprint(f\"{flight.code}: {flight.origin} to {flight.destination}\")\nprint(flight.minutes // 60, \"h\", flight.minutes % 60, \"min\")\n```\n\nprints\n\n```\nPA101: Lisbon to Oslo\n3 h 50 min\n```\n","kind":"code","seed":"# Write the class Flight here.\n\n\nif __name__ == \"__main__\":\n    flight = Flight(\"PA101\", \"Lisbon\", \"Oslo\", 230)\n    print(flight.code, flight.minutes)\n","solution":"class Flight:\n    def __init__(self, code, origin, destination, minutes):\n        self.code = code\n        self.origin = origin\n        self.destination = destination\n        self.minutes = minutes\n\n\nif __name__ == \"__main__\":\n    flight = Flight(\"PA101\", \"Lisbon\", \"Oslo\", 230)\n    print(flight.code, flight.minutes)\n","hints":["Start with `class Flight:`, and inside it `def __init__(self, code, origin, destination, minutes):`.","In the constructor, store each parameter in an attribute of the same name: `self.code = code`, and so on for all four."],"tests":[{"name":"Test 1","stdin":"","call":"flight = Flight(\"PA101\", \"Lisbon\", \"Oslo\", 230)\nprint(f\"{flight.code}: {flight.origin} to {flight.destination}\")\nprint(flight.minutes // 60, \"h\", flight.minutes % 60, \"min\")\n","expect":"PA101: Lisbon to Oslo\n3 h 50 min","hidden":false},{"name":"Test 2","stdin":"","call":"short = Flight(\"PA7\", \"Turku\", \"Stockholm\", 55)\nlong = Flight(\"PA900\", \"Paris\", \"Tokyo\", 805)\nprint(short.code, short.origin, short.destination, short.minutes)\nprint(long.code, long.origin, long.destination, long.minutes)\n","expect":"PA7 Turku Stockholm 55\nPA900 Paris Tokyo 805","hidden":true}],"require":[],"forbid":[]}]}