9.9. OOP Type Annotation

9.9.1. Rationale

  • All classes are types

  • Always depend upon abstraction not an implementation (SOLID Dependency Inversion Principle). More information in S.O.L.I.D.

class Astronaut:
    pass


mark: Astronaut = Astronaut()
class Cache:
    pass

class DatabaseCache(Cache):
    pass

class MemoryCache(Cache):
    pass

class FilesystemCache(Cache):
    pass


db: Cache = DatabaseCache()
mem: Cache = MemoryCache()
fs: Cache = FilesystemCache()

9.9.2. Attributes

class Point:
    x: int
    y: int

    def __init__(self, x, y):
        self.x = x
        self.y = y
class Point:
    def __init__(self, x, y):
        self.x: int = x
        self.y: int = y

9.9.3. Method Return Types

class Astronaut:
    def say_hello(self) -> str:
        return 'My name... José Jiménez'


mark: Astronaut = Astronaut()
mark.say_hello()
# 'My name... José Jiménez'
class Point:
    def get_coordinates(self) -> tuple[int, int]:
        return 1, 2


pt: Point = Point()
pt.get_coordinates()
# (1, 2)

9.9.4. Required Method Arguments

class Point:
    def __init__(self, x: int, y: int) -> None:
        self.x = x
        self.y = y
class Astronaut:
    def __init__(self, firstname: str, lastname: str) -> None:
        self.firstname: str = firstname
        self.lastname: str = lastname

9.9.5. Optional Method Arguments

class Point:
    def __init__(self, x: int = 0, y: int = 0) -> None:
        self.x = x
        self.y = y

    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


pt: Point = Point()
pt.set_coordinates(1, 2)
pt.get_coordinates()
# (1, 2)

9.9.6. Classes

class Point:
    def __init__(self, x: int = 0, y: int = 0) -> None:
        self.x = x
        self.y = y

    def __str__(self) -> str:
        return f'Point(x={self.x}, y={self.y})'


class Position:
    def __init__(self, initial_position: Point = Point()) -> None:
        self.position = initial_position

    def get_coordinates(self) -> Point:
        return self.position


pos: Position = Position()

pos.get_coordinates()
# <__main__.Point object at 0x11c5531c0>

print(pos.get_coordinates())
# Point(x=0, y=0)

9.9.7. Nested

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'),
]

9.9.8. Final Class

New in version Python: 3.8 See PEP 591

from typing import final


@final
class Astronaut:
    pass
Listing 9.41. Error: Cannot inherit from final class "Base"
from typing import final


@final
class Astronaut:
    pass

class Pilot(Astronaut):
    pass

9.9.9. Final Method

New in version Python: 3.8 See PEP 591

from typing import final


class Astronaut:

    @final
    def say_hello(self) -> None:
        pass
Listing 9.42. Error: Cannot override final attribute "foo" (previously declared in base class "Base")
from typing import final


class Astronaut:
    @final
    def say_hello(self) -> None:
        pass

class Pilot(Astronaut):
    def say_hello(self) -> None:    # Error: Cannot override final attribute
        pass

9.9.10. Final Attribute

from typing import Final


class Position:
    x: Final[int]
    y: Final[int]

    def __init__(self) -> None:
        self.x = 1
        self.y = 2
Listing 9.43. Error: final attribute (y) without an initializer
from typing import Final


class Position:
    x: Final[int]
    y: Final[int]       # Error: final attribute 'y' without an initializer

    def __init__(self) -> None:
        self.x = 1
Listing 9.44. Error: can't override a final attribute
from typing import Final


class Settings:
    RESOLUTION_X_MIN: Final[int] = 0
    RESOLUTION_X_MAX: Final[int] = 1024
    RESOLUTION_Y_MIN: Final[int] = 0
    RESOLUTION_Y_MAX: Final[int] = 768


class Game(Settings):
    RESOLUTION_X_MIN = 3        # Error: can't override a final attribute
Listing 9.45. Error: can't override a final attribute
from typing import Final


class Hero:
    DAMAGE_MIN: Final[int] = 10
    DAMAGE_MAX: Final[int] = 20


Hero.DAMAGE_MIN = 1             # Error: can't override a final attribute

9.9.11. More Information

Note

More information in Type Annotations and Type Checking