3.1. Type Annotation Scalar

Types are not required, and never will be. -- Guido van Rossum, Python initiator, core developer, former BDFL

It should be emphasized that Python will remain a dynamically typed language, and the authors have no desire to ever make type hints mandatory, even by convention. -- Python Software Foundation

3.1.1. Rationale

3.1.2. Int

>>> data: int = 0
>>> data: int = 1
>>> data: int = -1

3.1.3. Float

>>> data: float = 0.0
>>> data: float = 1.23
>>> data: float = -1.23

3.1.4. Str

>>> data: str = ''
>>> data: str = 'Jan Twardowski'

3.1.5. Bool

>>> data: bool = True
>>> data: bool = False

3.1.6. Union

>>> from typing import Union
>>>
>>> number: Union[int, float] = 1337
>>> number: Union[int, float] = 1.337

Since Python 3.10: PEP 604 -- Allow writing union types as X | Y

>>> number: int|float = 1337        
>>> number: int|float = 1.337       
>>> number: int|None = 1337         
>>> number: int|None = None         

Result of this expression would then be valid in isinstance() and issubclass()

>>> isinstance(1337, int|float)     
>>> isinstance(1337, int|None)      

3.1.7. Optional

>>> from typing import Optional
>>>
>>> data: Optional[int] = 1337
>>> data: Optional[int] = None
>>> from typing import Optional
>>>
>>> firstname: str = 'Melissa'
>>> lastname: str = 'Lewis'
>>> age: Optional[float] = None

Since Python 3.11: PEP 645 -- Allow writing optional types as x?

>>> age: int? = 1337                
>>> age: int? = None                

Result of this expression would then be valid in isinstance() and issubclass()

>>> isinstance(1337, int?)          

3.1.8. Aliases

>>> from typing import Union
>>>
>>> Number = Union[float, int]
>>> age: Number = 10
>>> age: Number = 10.5

Since Python 3.10 PEP 613 -- TypeAlias Annotation

PEP 484 introduced the concept of type aliases, only requiring them to be top-level unannotated assignments. This simplicity sometimes made it difficult for type checkers to distinguish between type aliases and ordinary assignments, especially when forward references or invalid types were involved. Compare:

>>> StrCache = 'Cache[str]'  # a type alias                 
>>> LOG_PREFIX = 'LOG[DEBUG]'  # a module constant          

Now the typing module has a special annotation TypeAlias to declare type aliases more explicitly:

>>> StrCache: TypeAlias = 'Cache[str]'  # a type alias      
>>> LOG_PREFIX = 'LOG[DEBUG]'  # a module constant          

Since Python 3.10:

>>> 
... from typing import TypeAlias
...
...
... Timestamp: TypeAlias = float

Before Python 3.10:

>>> Timestamp = float

3.1.9. Type Vars

>>> from typing import TypeVar, Iterable, Tuple
>>>
>>>
>>> T = TypeVar('T', int, float, complex)
>>> Vector = Iterable[tuple[T, T]]
>>>
>>> def product(data: Vector[T]) -> T:
...     return sum(x*y for x,y in data)
>>> from typing import TypeVar, Iterable, Tuple
>>>
>>>
>>> T = TypeVar('T', int, float, complex)
>>> Vector = Iterable[tuple[T, T]]
>>>
>>> def dilate(data: Vector[T], scale: T) -> Vector[T]:
...     return ((x*scale, y*scale) for x,y in data)

3.1.10. Final

Since Python 3.8: PEP 591 -- Adding a final qualifier to typing

>>> from typing import Final
>>>
>>> m: Final[int] = 1
>>> km: Final[int] = 1000 * m
>>> from typing import Final
>>>
>>> second: Final[int] = 1
>>> minute: Final[int] = 60 * second
>>> hour: Final[int] = 60 * minute
>>> day: Final[int] = 24 * hour

3.1.11. Types are not Enforced

  • This code will run without any problems

  • Although mypy or pyre-check will throw error

>>> name: int = 'Jan Twardowski'
>>> age: float = 30
>>> is_adult: int = True