3.1. Typing About
Also known as: "type annotations", "type hints", "gradual typing"
Types are not required, and never will be
Good IDE will give you hints
Types are used extensively in system libraries
More and more books and documentations use types
Introduced in Python 3.5
To type check use:
mypy,pyre-check,pytypes
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
Figure 3.2. Timeline of changes to type annotations from Python 3.0 to now [1]
3.1.1. Problem
>>> def add_numbers(a, b):
... return a + b
>>>
>>>
>>> add_numbers(1, 2)
3
>>> add_numbers(1.0, 2.0)
3.0
>>> add_numbers('a', 'b')
'ab'
>>> add_numbers(['a'], ['b'])
['a', 'b']
3.1.2. Runtime Validation
if+isinstance()checks the type at runtimeif+type()checks the type at runtimeasserttype()checks the type at runtimeraises
TypeErrorif the type is wrong
if + isinstance():
>>> def add_numbers(a, b):
... if not isinstance(a, int|float):
... raise TypeError('a must be int or float')
... if not isinstance(b, int|float):
... raise TypeError('b must be int or float')
... return a + b
>>>
>>>
>>> add_numbers(1, 2)
3
>>> add_numbers(1.0, 2.0)
3.0
>>> add_numbers('a', 'b')
Traceback (most recent call last):
TypeError: a must be int or float
>>>
>>> add_numbers(['a'], ['b'])
Traceback (most recent call last):
TypeError: a must be int or float
if + type():
>>> def add_numbers(a, b):
... if type(a) not in (int, float):
... raise TypeError('a must be int or float')
... if type(b) not in (int, float):
... raise TypeError('b must be int or float')
... return a + b
>>>
>>>
>>> add_numbers(1, 2)
3
>>> add_numbers(1.0, 2.0)
3.0
>>> add_numbers('a', 'b')
Traceback (most recent call last):
TypeError: a must be int or float
>>>
>>> add_numbers(['a'], ['b'])
Traceback (most recent call last):
TypeError: a must be int or float
assert + type():
>>> def add_numbers(a, b):
... assert type(a) in (int, float), 'a must be int or float'
... assert type(b) in (int, float), 'b must be int or float'
... return a + b
>>>
>>>
>>> add_numbers(1, 2)
3
>>> add_numbers(1.0, 2.0)
3.0
>>> add_numbers('a', 'b')
Traceback (most recent call last):
AssertionError: a must be int or float
>>>
>>> add_numbers(['a'], ['b'])
Traceback (most recent call last):
AssertionError: a must be int or float
3.1.3. Static Validation
>>> def add_numbers(a: int|float, b: int|float) -> int|float:
... return a + b
>>>
>>>
>>> add_numbers(1, 2)
3
>>> add_numbers(1.0, 2.0)
3.0
>>> add_numbers('a', 'b')
'ab'
>>> add_numbers(['a'], ['b'])
['a', 'b']
3.1.4. Python 2.X
>>> def add(a, b):
... """
... :param a: int
... :param b: int
... :return: int
... """
... return a + b
3.1.5. Python 2.7
>>> def add(a, b):
... # (int, int) -> int
... return a + b
3.1.6. Python 3.0
>>> def add(a: 'int', b: 'int') -> 'int':
... return a + b
3.1.7. Python 3.5
>>> def add(a: int, b: int) -> int:
... return a + b
3.1.8. Typing PEPs
Since Python 3.5: PEP 484 -- Type Hints
Since Python 3.6: PEP 526 -- Syntax for Variable Annotations
Since Python 3.8: PEP 544 -- Protocols: Structural subtyping (static duck typing)
Since Python 3.9: PEP 585 -- Type Hinting Generics In Standard Collections
Since Python 3.10: PEP 604 -- Allow writing union types as X | Y
PEP 482 -
literature overview on type hintsPEP 483 -
background on type hintsPEP 484 -
type hintsPEP 526 -
variable annotations and ClassVarPEP 544 -
ProtocolPEP 561 -
distributing typed packagesPEP 563 -
from __future__ import annotationsPEP 585 -
subscriptable generics in the standard libraryPEP 586 -
LiteralPEP 589 -
TypedDictPEP 591 -
FinalPEP 593 -
AnnotatedPEP 604 -
union syntax with |PEP 612 -
ParamSpecPEP 613 -
TypeAliasPEP 646 -
variadic generics and TypeVarTuplePEP 647 -
TypeGuardPEP 649 -
(draft), from __future__ import co_annotationsPEP 655 -
Required and NotRequiredPEP 673 -
SelfPEP 675 -
LiteralStringPEP 677 -
(rejected), (int, str) -> bool callable type syntaxPEP 681 -
@dataclass_transform()PEP 688 -
BufferPEP 692 -
Unpack[TypedDict] for **kwargsPEP 695 -
class Class[T]: type parameter syntaxPEP 696 -
(draft), defaults for type variablesPEP 698 -
@overridePEP 702 -
(draft), @deprecated()PEP 705 -
(draft), TypedMapping
3.1.9. Errors
Types are not Enforced
This code will run without any problems
Types are not required, and never will be
Although
mypy,pyre-checkorpytypeswill throw error
>>> def add(a: int, b: int) -> int:
... return a + b
>>>
>>>
>>> add(1, 2)
3
>>>
>>> add(1.0, 2.0)
3.0
>>>
>>> add('a', 'b')
'ab'
3.1.10. Annotation
Annotation and definition can be separated
Two step process:
>>> x: int
>>> x = 1
One liner:
>>> x: int = 1
3.1.11. Annotation is not Definition
Two step process:
>>> print(a)
Traceback (most recent call last):
NameError: name 'a' is not defined
>>>
>>> a: int
>>>
>>> print(a)
Traceback (most recent call last):
NameError: name 'a' is not defined
>>>
>>> a = 1
>>> print(a)
1
Oneliner:
>>> print(b)
Traceback (most recent call last):
NameError: name 'b' is not defined
>>>
>>> b: int = 1
>>>
>>> print(b)
1
Even if the type is wrong, the code will run and the value will be assigned:
>>> print(c)
Traceback (most recent call last):
NameError: name 'c' is not defined
>>>
>>> c: int = 'one'
>>>
>>> print(c)
one
3.1.12. Dynamic Typing
good: fast development
bad: runtime errors (in projects with many developers)
>>> def add(a, b):
... return a + b
3.1.13. Static Typing
good: no runtime errors
bad: slow development
int add(int a, int b) {
return a + b;
}
float add(float a, float b) {
return a + b;
}
float add(int a, float b) {
return (float)a + b;
}
float add(float a, int b) {
return a + (float)b;
}
3.1.14. Gradual Typing
good: fast development
good: no runtime errors
Start:
>>> def add(a, b):
... return a + b
Later:
>>> def add(a: int, b: int):
... return a + b
Later:
>>> def add(a: int, b: int) -> int:
... return a + b
Later:
>>> def add(a: int|float, b: int|float) -> int|float:
... return a + b
3.1.15. Checkers
mypy- the reference implementation for type checkerspyre- written in OCaml and optimized for performancepyright- a type checker that emphasizes speedpytype- checks and infers types for unannotated code