16.8. OOP Init Setattr

>>> class User:
...     firstname: str
...     lastname: str
...
...     def __init__(self, firstname, lastname):
...         self.firstname = firstname
...         self.lastname = lastname
>>>
>>>
>>> mark = User('Mark', 'Watney')
>>>
>>> vars(mark)
{'firstname': 'Mark', 'lastname': 'Watney'}

16.8.1. Constant Attributes

>>> class User:
...     def __init__(self):
...         self.firstname = 'Mark'
...         self.lastname = 'Watney'
>>> mark = User()
>>> melissa = User()
>>> vars(mark)
{'firstname': 'Mark', 'lastname': 'Watney'}
>>>
>>> vars(melissa)
{'firstname': 'Mark', 'lastname': 'Watney'}

16.8.2. Variable Attributes

>>> class User:
...     def __init__(self, a, b):
...         self.firstname = a
...         self.lastname = b
>>> mark = User('Mark', 'Watney')
>>> vars(mark)
{'firstname': 'Mark', 'lastname': 'Watney'}
>>> mark = User(a='Mark', b='Watney')
>>> vars(mark)
{'firstname': 'Mark', 'lastname': 'Watney'}

16.8.3. Better Names

>>> class User:
...     def __init__(self, firstname, lastname):
...         self.firstname = firstname
...         self.lastname = lastname
>>> mark = User('Mark', 'Watney')
>>> vars(mark)
{'firstname': 'Mark', 'lastname': 'Watney'}
>>> mark = User(firstname='Mark', lastname='Watney')
>>> vars(mark)
{'firstname': 'Mark', 'lastname': 'Watney'}

16.8.4. Combine Attributes

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

16.8.5. Use Case - 0x01

>>> class Point:
...     def __init__(self, x, y):
...         self.x = x
...         self.y = y
>>>
>>>
>>> a = Point(10, 20)
>>> b = Point(10, y=20)
>>> c = Point(x=10, y=20)
>>> d = Point(y=20, x=10)

16.8.6. Use Case - 0x02

>>> class Point:
...     def __init__(self, x, y, z=0):
...         self.x = x
...         self.y = y
...         self.z = z
>>> a = Point(10, 20)
>>> b = Point(10, y=20)
>>> c = Point(x=10, y=20)
>>> d = Point(y=20, x=10)
>>> e = Point(10, 20, 30)
>>> f = Point(10, 20, z=30)
>>> g = Point(10, y=20, z=30)
>>> h = Point(10, z=30, y=20)
>>> i = Point(x=10, y=20, z=30)
>>> j = Point(x=10, z=30, y=20)
>>> k = Point(y=20, x=10, z=30)
>>> l = Point(y=20, z=30, x=10)
>>> m = Point(z=30, x=10, y=20)
>>> n = Point(z=30, y=20, x=10)

16.8.7. Use Case - 0x03

>>> 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')
>>> virginica = Iris(
...     sepal_length=5.8,
...     sepal_width=2.7,
...     petal_length=5.1,
...     petal_width=1.9,
...     species='virginica')

16.8.8. Use Case - 0x04

  • Dataclasses

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'
>>>
>>>
>>> virginica = Iris(
...     sepal_length=5.8,
...     sepal_width=2.7,
...     petal_length=5.1,
...     petal_width=1.9,
...     species='virginica')
>>>
>>> vars(virginica)  
{'sepal_length': 5.8,
 'sepal_width': 2.7,
 'petal_length': 5.1,
 'petal_width': 1.9,
 'species': 'virginica'}

16.8.9. Use Case - 0x05

>>> from random import randint, seed
>>> seed(0)
>>>
>>>
>>> class Hero:
...     name: str
...     position: tuple[int,int]
...     health: int
...
...     def __init__(self, name, position_x, position_y):
...         self.name = name
...         self.position = (position_x, position_y)
...         self.health = randint(50,100)
>>>
>>>
>>> mark = Hero('Mark', position_x=10, position_y=20)
>>> vars(mark)
{'name': 'Mark', 'position': (10, 20), 'health': 74}

16.8.10. Use Case - 0x06

>>> from random import randint, seed
>>> seed(0)
>>>
>>>
>>> class Hero:
...     name: str
...     position: tuple[int,int]
...     health: int
...
...     def __init__(self, name, health_min=10, health_max=100):
...         self.name = name
...         self.position = (0, 0)
...         self.health = randint(health_min,health_max)
>>>
>>>
>>> mark = Hero('Mark', health_min=50)
>>> vars(mark)
{'name': 'Mark', 'position': (0, 0), 'health': 74}

16.8.11. Assignments

Code 16.11. Solution
"""
* Assignment: OOP Init Define
* Type: class assignment
* Complexity: easy
* Lines of code: 6 lines
* Time: 5 min

English:
    1. Modify code below
    2. Implement method `__init__()` to set attributes:
       `self.name`, `self.country`, `self.date`
    3. Attributes must be set from arguments passed at initialization
    4. Run doctests - all must succeed

Polish:
    1. Zmodyfikuj kod poniżej
    2. Zaimplementuj metodę `__init__()` aby ustawiała atrybuty:
       `self.name`, `self.country`, `self.date`
    3. Atrybuty muszą być ustawiane z argumentów podanych przy inicjalizacji
    4. Uruchom doctesty - wszystkie muszą się powieść

Tests:
    >>> import sys; sys.tracebacklimit = 0
    >>> from inspect import ismethod, signature

    >>> mark = Astronaut('Mark', 'USA', '1969-07-21')
    >>> nasa = Astronaut('Nasa', 'USA', '1969-07-21')

    >>> assert ismethod(mark.__init__)
    >>> assert ismethod(nasa.__init__)

    >>> signature(Astronaut.__init__)
    <Signature (self, name, country, date)>
    >>> signature(SpaceAgency.__init__)
    <Signature (self, name, country, date)>

    >>> signature(mark.__init__)
    <Signature (name, country, date)>
    >>> signature(nasa.__init__)
    <Signature (name, country, date)>
"""


# Implement method to set: self.name, self.country, self.date
# Attributes must be set from arguments passed to __init__()
# type: type[Astronaut]
class Astronaut:
    name: str
    country: str
    date: str

    def __init__(self, name, country, date):
        raise NotImplementedError


# Implement method to set: self.name, self.country, self.date
# Attributes must be set from arguments passed to __init__()
# type: type[Astronaut]
class SpaceAgency:
    name: str
    country: str
    date: str

    def __init__(self, name, country, date):
        raise NotImplementedError


Code 16.12. Solution
"""
* Assignment: OOP Init SetAttrPositional
* Type: class assignment
* Complexity: easy
* Lines of code: 2 lines
* Time: 3 min

English:
    1. Modify code below
    2. Create instances of an `Astronaut` and `SpaceAgency` classes
    3. Use positional arguments to pass values at the initialization
    4. Run doctests - all must succeed

Polish:
    1. Zmodyfikuj kod poniżej
    2. Stwórz instancje klas `Astronaut` i `SpaceAgency`
    3. Użyj argumentów pozycyjnych do przekazania wartości przy inicjalizacji
    4. Uruchom doctesty - wszystkie muszą się powieść

Tests:
    >>> import sys; sys.tracebacklimit = 0

    >>> assert isinstance(mark, Astronaut)
    >>> assert isinstance(nasa, SpaceAgency)
    >>> assert 'Mark' in vars(mark).values()
    >>> assert 'USA' in vars(mark).values()
    >>> assert '1969-07-21' in vars(mark).values()
    >>> assert 'Nasa' in vars(nasa).values()
    >>> assert 'USA' in vars(nasa).values()
    >>> assert '1969-07-21' in vars(nasa).values()
"""


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


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


# use positional arguments to create instance with: Mark, USA, 1969-07-21
# type: Astronaut
mark = ...

# use positional arguments to create instance with: Nasa, USA, 1969-07-21
# type: SpaceAgency
nasa = ...


Code 16.13. Solution
"""
* Assignment: OOP Init SetAttrKeyword
* Type: class assignment
* Complexity: easy
* Lines of code: 2 lines
* Time: 3 min

English:
    1. Modify code below
    2. Create instances of an `Astronaut` and `SpaceAgency` classes
    3. Use keyword arguments to pass values at the initialization
    4. Run doctests - all must succeed

Polish:
    1. Zmodyfikuj kod poniżej
    2. Stwórz instancje klas `Astronaut` i `SpaceAgency`
    3. Użyj argumentów nazwanych do przekazania wartości przy inicjalizacji
    4. Uruchom doctesty - wszystkie muszą się powieść

Tests:
    >>> import sys; sys.tracebacklimit = 0

    >>> assert isinstance(mark, Astronaut)
    >>> assert isinstance(nasa, SpaceAgency)
    >>> assert 'Mark' in vars(mark).values()
    >>> assert 'USA' in vars(mark).values()
    >>> assert '1969-07-21' in vars(mark).values()
    >>> assert 'Nasa' in vars(nasa).values()
    >>> assert 'USA' in vars(nasa).values()
    >>> assert '1969-07-21' in vars(nasa).values()
"""


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


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


# use keyword arguments to create instance with: Mark, USA, 1969-07-21
# type: Astronaut
mark = ...

# use keyword arguments to create instance with: Nasa, USA, 1969-07-21
# type: SpaceAgency
nasa = ...