9.4. OOP Init Method

9.4.1. Rationale

  • It's a first method run after object is initiated

  • All classes has default __init__()

constructor

Method called at object instantiation used to create object. Constructor is called on not fully initialized object and hence do not have access to object methods. Constructor should return None.

initializer

Method called at object instantiation used to fill empty object with values. Initializer is called upon object initialization and hence can modify object and use its methods. Initializer should return None.

9.4.2. Syntax

>>> class Astronaut:
...     firstname: str
...     lastname: str
...
...     def __init__(self, firstname, lastname):
...         self.firstname = firstname
...         self.lastname = lastname
>>>
>>>
>>> astro = Astronaut('Mark', 'Watney')
>>>
>>> print(astro.firstname)
Mark
>>> print(astro.lastname)
Watney

9.4.3. Initializer Method Without Arguments

Initializer method without arguments:

>>> class Astronaut:
...     def __init__(self):
...         print('My name... José Jiménez')
>>>
>>>
>>> jose = Astronaut()
My name... José Jiménez

9.4.4. Initializer Method With Arguments

Initializer method with arguments:

>>> class Astronaut:
...     def __init__(self, firstname, lastname='Unknown'):
...         print(f'My name... {firstname} {lastname}')
>>>
>>>
>>> jan = Astronaut('Jan')
My name... Jan Unknown
>>> jose = Astronaut('José', 'Jiménez')
My name... José Jiménez
>>> melissa = Astronaut('Melissa', lastname='Lewis')
My name... Melissa Lewis
>>> mark = Astronaut(firstname='José', lastname='Jiménez')
My name... José Jiménez
>>> ryan = Astronaut(lastname='Stone', firstname='Ryan')
My name... Ryan Stone
>>> ivan = Astronaut()
Traceback (most recent call last):
TypeError: __init__() missing 1 required positional argument: 'firstname'

9.4.5. Constant Attributes

Init time attributes:

>>> class Astronaut:
...     def __init__(self):
...         self.firstname = 'Mark'
...         self.lastname = 'Watney'
>>>
>>>
>>> mark = Astronaut()
>>> print(mark.firstname)
Mark
>>> print(mark.lastname)
Watney
>>> print(mark.missions)
Traceback (most recent call last):
AttributeError: 'Astronaut' object has no attribute 'mission'
>>>
>>> ivan = Astronaut()
>>> print(ivan.firstname)
Mark
>>> print(ivan.lastname)
Watney
>>> print(ivan.missions)
Traceback (most recent call last):
AttributeError: 'Astronaut' object has no attribute 'mission'

9.4.6. Variable Attributes

>>> class Astronaut:
...     def __init__(self, a, b):
...         self.firstname = a
...         self.lastname = b
>>>
>>>
>>> mark = Astronaut('Mark', 'Watney')
>>> print(mark.firstname)
Mark
>>> print(mark.lastname)
Watney
>>>
>>> ivan = Astronaut(a='Ivan', b='Ivanovich')
>>> print(ivan.firstname)
Ivan
>>> print(ivan.lastname)
Ivanovich

Init time attributes:

>>> class Astronaut:
...     def __init__(self, firstname, lastname):
...         self.firstname = firstname
...         self.lastname = lastname
>>>
>>>
>>> mark = Astronaut('Mark', 'Watney')
>>> print(mark.firstname)
Mark
>>> print(mark.lastname)
Watney
>>>
>>> ivan = Astronaut(firstname='Ivan', lastname='Ivanovich')
>>> print(ivan.firstname)
Ivan
>>> print(ivan.lastname)
Ivanovich

Init time attributes:

>>> class Astronaut:
...     def __init__(self, firstname, lastname):
...         self.name = f'{firstname} {lastname}'
>>>
>>>
>>> mark = Astronaut('Mark', 'Watney')
>>>
>>> print(mark.name)
Mark Watney
>>> print(mark.firstname)
Traceback (most recent call last):
AttributeError: 'Astronaut' object has no attribute 'firstname'
>>> print(mark.lastname)
Traceback (most recent call last):
AttributeError: 'Astronaut' object has no attribute 'lastname'

Init time attributes:

>>> class Point:
...     def __init__(self, x, y, z=0):
...         self.x = x
...         self.y = y
...         self.z = z
>>>
>>>
>>> p1 = Point(10, 20)
>>> p2 = Point(x=10, y=20)
>>> p3 = Point(10, 20, 30)
>>> p4 = Point(10, 20, z=30)
>>> p5 = Point(x=10, y=20, z=30)

Init time attributes:

>>> class Iris:
...     def __init__(self, sepal_length, sepal_width,
...                  petal_length, petal_width, species):
...
...         self.sepal_length = sepal_length
...         self.sepal_width = sepal_width
...         self.petal_length = petal_length
...         self.petal_width = petal_width
...         self.species = species
>>>
>>>
>>> setosa = Iris(5.1, 3.5, 1.4, 0.2, 'setosa')
>>>
>>> print(setosa.sepal_length)
5.1
>>> print(setosa.sepal_width)
3.5
>>> print(setosa.petal_length)
1.4
>>> print(setosa.petal_width)
0.2
>>> print(setosa.species)
setosa
>>>
>>>
>>> virginica = Iris(
...     sepal_length=5.8,
...     sepal_width=2.7,
...     petal_length=5.1,
...     petal_width=1.9,
...     species='virginica')
>>>
>>> print(virginica.__dict__)  # doctest: +NORMALIZE_WHITESPACE
{'sepal_length': 5.8,
 'sepal_width': 2.7,
 'petal_length': 5.1,
 'petal_width': 1.9,
 'species': 'virginica'}

Since Python 3.7: there is a @dataclass decorator, which automatically generates __init__() arguments and fields. More information in OOP Dataclass.

>>> from dataclasses import dataclass
>>>
>>>
>>> @dataclass
... class Iris:
...     sepal_length: float
...     sepal_width: float
...     petal_length: float
...     petal_width: float
...     species: str = 'Iris'
>>>
>>>
>>> setosa = Iris(5.1, 3.5, 1.4, 0.2, 'setosa')
>>>
>>> print(setosa.sepal_length)
5.1
>>> print(setosa.sepal_width)
3.5
>>> print(setosa.petal_length)
1.4
>>> print(setosa.petal_width)
0.2
>>> print(setosa.species)
setosa
>>>
>>>
>>> virginica = Iris(
...     sepal_length=5.8,
...     sepal_width=2.7,
...     petal_length=5.1,
...     petal_width=1.9,
...     species='virginica')
>>>
>>> print(virginica.__dict__)  # doctest: +NORMALIZE_WHITESPACE
{'sepal_length': 5.8,
 'sepal_width': 2.7,
 'petal_length': 5.1,
 'petal_width': 1.9,
 'species': 'virginica'}

9.4.7. Checking Values

>>> class Kelvin:
...     MINIMAL_VALUE = 0.0
...
...     def __init__(self, value):
...         if value < self.MINIMAL_VALUE:
...             raise ValueError('Temperature must be greater than 0')
...         self.value = value
>>>
>>>
>>> t1 = Kelvin(273.15)
>>> print(t1.value)
273.15
>>>
>>> t2 = Kelvin(-300)
Traceback (most recent call last):
ValueError: Temperature must be greater than 0

9.4.8. Assignments

Code 9.7. Solution
"""
* Assignment: OOP Init Print
* Complexity: easy
* Lines of code: 3 lines
* Time: 3 min

English:
    1. Create one class `Echo`
    2. Value `text` must be passed at the initialization
    3. At initialization instance print `text`
    4. Do not store values in the instances (only print on instance creation)
    5. Do not use `@dataclass`
    6. Compare result with "Tests" section (see below)

Polish:
    1. Stwórz jedną klasę `Echo`
    2. Wartość `text` maja być podawana przy inicjalizacji
    3. Przy inicjalizacji instancja wypisuje `text`
    4. Nie przechowuj informacji w instancjach (tylko wypisz przy inicjalizacji)
    5. Nie używaj `@dataclass`
    6. Porównaj wyniki z sekcją "Tests" (patrz poniżej)

Tests:
    >>> _ = Echo('hello')
    hello
    >>> _ = Echo('world')
    world
    >>> result = Echo('Test')
    Test
    >>> result.__dict__
    {}
"""


Code 9.8. Solution
"""
* Assignment: OOP Init Model
* Complexity: easy
* Lines of code: 12 lines
* Time: 8 min

English:
    1. Use data from "Given" section (see below)
    2. Model the data using classes
    3. Create instances for each record
    4. Values must be passed at the initialization
    5. Create instances of a first class using positional arguments
    6. Create instances of a second class using keyword arguments
    7. Do not use `@dataclass`
    8. Compare result with "Tests" section (see below)

Polish:
    1. Użyj danych z sekcji "Given" (patrz poniżej)
    2. Zamodeluj dane za pomocą klas
    3. Stwórz instancje dla każdego wpisu
    4. Wartości mają być podawane przy inicjalizacji
    5. Twórz instancje pierwszej klasy używając argumentów pozycyjnych
    6. Twórz instancje drugiej klasy używając argumentów nazwanych
    7. Nie używaj `@dataclass`
    8. Porównaj wyniki z sekcją "Tests" (patrz poniżej)

Tests:
    >>> assert isinstance(watney, Astronaut)
    >>> assert isinstance(nasa, SpaceAgency)
    >>> assert 'Watney' in watney.__dict__.values()
    >>> assert 'USA' in watney.__dict__.values()
    >>> assert '1969-07-21' in watney.__dict__.values()
    >>> assert 'NASA' in nasa.__dict__.values()
    >>> assert 'USA' in nasa.__dict__.values()
    >>> assert '1958-07-29' in nasa.__dict__.values()
"""


# Given
"""
Watney, USA, 1969-07-21
NASA, USA, 1958-07-29
"""