18.10. Typing Type

  • All classes are types

>>> class Account:
...     firstname: str
...     lastname: str
...
...     def __init__(self, firstname: str, lastname: str) -> None:
...         self.firstname = firstname
...         self.lastname = lastname
...
...     def login(self, username: str, password: str | None = None) -> None:
...         ...
...
...     def logout(self) -> None:
...         ...
>>>
>>>
>>> class User(Account):
...     pass
>>>
>>> class Admin(Account):
...     pass
>>>
>>>
>>> a: Account = User('Alice', 'Apricot')
>>> b: Account = Admin('Bob', 'Blackthorn')

18.10.1. Instance

>>> class User:
...     pass
>>>
>>>
>>> a: User = User()
>>> b: User = User()

18.10.2. Inheritance

  • Dependency Inversion Principle

  • Always depend upon abstraction not an implementation

  • More information in OOP SOLID

>>> class Account:
...     pass
>>>
>>> class User(Account):
...     pass
>>>
>>> class Admin(Account):
...     pass
>>>
>>>
>>> a: Account = User()
>>> b: Account = Admin()

18.10.3. Instance Variables

>>> class User:
...     firstname: str
...     lastname: str
>>>
>>>
>>> a = User()
>>> a.firstname = 'Alice'
>>> a.lastname = 'Apricot'

18.10.4. Class Variables

>>> from typing import ClassVar
>>>
>>> class User:
...     AGE_MIN: ClassVar[int] = 0
...     AGE_MAX: ClassVar[int] = 130

18.10.5. Method Return Type

>>> class User:
...     def login(self) -> None:
...         ...

18.10.6. Required Method Arguments

>>> class User:
...     def login(self, username: str, password: str):
...         ...

18.10.7. Optional Method Arguments

>>> class User:
...     def login(self, username: str, password: str | None = None):
...         ...

18.10.8. Init Method

>>> class User:
...     firstname: str
...     lastname: str
...
...     def __init__(self, firstname: str, lastname: str) -> None:
...         self.firstname = firstname
...         self.lastname = lastname

18.10.9. Recap

  • Type annotations

  • Type hints

  • Optional, but a good practice

  • Python does not verify if values have correct types

  • IDEs such as PyCharm, VS Code or tools such as mypy can do that

No Attribute Definition:

>>> class User:
...     pass

Basic Types:

>>> class User:
...     firstname: str
...     lastname: str
...     age: int

Union:

>>> class User:
...     firstname: str
...     lastname: str
...     age: int | float

Optional:

>>> class User:
...     firstname: str
...     lastname: str
...     age: int | float
...     height: float | None
...     weight: float | None

Sequences:

>>> class User:
...     firstname: str
...     lastname: str
...     age: int
...     groups: list[str]

Relation One to One:

>>> class Group:
...     gid: int
...     name: int
>>>
>>>
>>> class User:
...     firstname: str
...     lastname: str
...     group: Group

Relation One to Many:

>>> class Group:
...     gid: int
...     name: str
>>>
>>>
>>> class User:
...     firstname: str
...     lastname: str
...     groups: list[Group]

18.10.10. Use Case - 1

>>> class Point:
...     x: int
...     y: int
...     z: int

18.10.11. Use Case - 2

>>> class Date:
...     year: int
...     month: int
...     day: int

18.10.12. Use Case - 3

>>> class Laptop:
...     cpu: str
...     ram: str
...     ssd: str

18.10.13. Use Case - 4

>>> class Iris:
...     features: list[float]
...     label: str

18.10.14. Use Case - 5

>>> class Iris:
...     sepal_length: float
...     sepal_width: float
...     petal_length: float
...     petal_width: float
...     species: str

18.10.15. Use Case - 6

>>> class User:
...     firstname: str
...     lastname: str
...     email: str
...     active: bool
...     age: int | float
...     height: float | None
...     weight: float | None
...     groups: list[str] | None
...     friends: list['User'] | None

18.10.16. Use Case - 7

>>> from datetime import date
>>> from typing import Literal
>>>
>>>
>>> class Address:
...     type: Literal['home', 'work']
...     street: str
...     house: str
...     apartment: str
...     postcode: str
...     city: str
...     region: str
...     country: str
>>>
>>>
>>> class PhoneNumber:
...     type: Literal['home', 'work', 'mobile']
...     number: str
>>>
>>>
>>> class User:
...     firstname: str
...     lastname: str
...     age: int | float
...     birthdate: date
...     gender: Literal['male', 'female']
...     height: float | None
...     weight: float | None
...     education: list[str] | None
...     job: str | None
...     addresses: list[Address] | None
...     emails: list[str] | None
...     phones: PhoneNumber | None
...     friends: list['Person'] | None

18.10.17. Use Case - 8

>>> class Point:
...     x: int
...     y: int
...
...     def set_coordinates(self, x: int, y: int) -> None:
...         self.x = x
...         self.y = y
...
...     def get_coordinates(self) -> tuple[int,int]:
...         return self.x, self.y

18.10.18. Use Case - 9

>>> class Point:
...     x: int
...     y: int
...
...     def __init__(self, x: int = 0, y: int = 0) -> None:
...         self.x = x
...         self.y = y
>>>
>>>
>>> class Position:
...     position: Point
...
...     def __init__(self, initial_position: Point = Point()) -> None:
...         self.position = initial_position
...
...     def get_coordinates(self) -> Point:
...         return self.position

18.10.19. Use Case - 10

  • SOLID Dependency Inversion Principle

>>> class Cache:
...     pass
>>>
>>> class DatabaseCache(Cache):
...     pass
>>>
>>> class MemoryCache(Cache):
...     pass
>>>
>>> class FilesystemCache(Cache):
...     pass
>>>
>>>
>>> a: Cache = DatabaseCache()
>>> b: Cache = MemoryCache()
>>> c: Cache = FilesystemCache()

18.10.20. Use Case - 11

>>> class Iris:
...     def __init__(self, features: list[float], label: str) -> None:
...         self.features: list[float] = features
...         self.label: str = label
>>>
>>> data: list[Iris] = [
...     Iris([4.7, 3.2, 1.3, 0.2], 'setosa'),
...     Iris([7.0, 3.2, 4.7, 1.4], 'versicolor'),
...     Iris([7.6, 3.0, 6.6, 2.1], 'virginica'),
... ]

18.10.21. Use Case - 12

>>> from typing import ClassVar
>>>
>>> class Settings:
...     RESOLUTION_X_MIN: ClassVar[int] = 0
...     RESOLUTION_X_MAX: ClassVar[int] = 1024
...     RESOLUTION_Y_MIN: ClassVar[int] = 0
...     RESOLUTION_Y_MAX: ClassVar[int] = 768

18.10.22. Use Case - 13

>>> from typing import ClassVar
>>>
>>>
>>> class Position:
...     x: int
...     y: int
>>>
>>>
>>> class Hero:
...     HEALTH_MIN: ClassVar[int] = 50
...     HEALTH_MAX: ClassVar[int] = 100
...     GOLD_MIN: ClassVar[int] = 50
...     GOLD_MAX: ClassVar[int] = 100
...     DAMAGE_MIN: ClassVar[int] = 5
...     DAMAGE_MAX: ClassVar[int] = 20
...     name: str
...     health: int
...     gold: int
...     position: Position

18.10.23. Use Case - 14

>>> from uuid import uuid4
>>> from typing import Final
>>>
>>>
>>> class User:
...     firstname: str
...     lastname: str
...     uuid: Final[uuid4]
...
...     def __init__(self, firstname: str, lastname: str, age: int) -> None:
...         self.firstname = firstname
...         self.lastname = lastname
...         self.uuid = uuid4()

18.10.24. Use Case - 15

>>> from typing import overload
>>>
>>>
>>> class User:
...     @overload
...     def login(self, username: str, password: str) -> None:
...         ...
...
...     @overload
...     def login(self, username: str, passkey: bytes) -> None:
...         ...