-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Python Typing Best Practices
from __future__ import annotations
def f() -> A: # forward reference
return A(0)
class A:
def __init__(self, x: int | str): # new syntax not supported at run time
self._x = x
def clone() -> A: # self reference
return A(self._x)
Otherwise all those types would need to be wrapped in quotes to prevent run time failures.
ID = str
def f(id: ID): pass
f("foo")
from typing import NewType
ID = NewType("ID", str)
x: ID = "foo"
error: Incompatible types in assignment (expression has type "str", variable has type "ID") [assignment]
x: ID = ID("foo")
from typing import TYPE_CHECKING
if TYPE_CHECKING:
import pandas as pd
else:
from bokeh.util.dependencies import import_optional
pd = import_optional("pandas")
Both are equivalent, but the later is Python 3.10 syntax, however usable with lazy annotations:
class A: pass
class B: pass
def f() -> A | B: pass
TypeError: unsupported operand type(s) for |: 'type' and 'type'
from __future__ import annotations
def f() -> A | B: pass
Prefer A | None
over Optional[A]
, because None
may not indicate semantic
optionality, but a perfectly valid value.
Note
A | B
syntax pre 3.10 may cause issues with tooling analyzing__annotations__
.
from typing import ClassVar, Set
class X:
known_instances: ClassVar[Set[X]] = set()
def __init__(self):
self.known_instances.add(self)
Imitate TypeScript's plain objects with optional fields, e.g. {a?: int, b?: string | null}
:
from typing import TypedDict
class Some(TypedDict, total=False):
a: int
b: str | None
assert Some() == {}
assert Some(b=None) == {"b": None}
Sadly can't make a subset of fields optional.
def f(x: Optional[int] = 0):
pass
If None
is not supported by the implementation, then don't use it to indicate optionality,
as that's already indicated by providing the default value.
We currently support Python 3.7, 3.8 and 3.9. For example:
- in Python 3.7:
>>> from typing import TypedDict
ImportError: cannot import name 'TypedDict' from 'typing'
>>> from typing_extensions import TypedDict
- in Python 3.8 and 3.9:
>>> from typing import TypedDict
>>> from typing_extensions import TypedDict
Other useful types missing in 3.7 are Literal
and Protocol
.
conda install -c conda-forge typing_extensions