Class type assignability¶
ClassVar
¶
(Originally specified in PEP 526.)
The typing.ClassVar
type qualifier is used to annotate
class variables that should not be set on class instances. This restriction
is enforced by static checkers, but not at runtime.
ClassVar
may be used in one of several forms:
With an explicit type, using the syntax
ClassVar[<type>]
. Example:class C: x: ClassVar[float] = 1
With no type annotation. Example:
class C: y: ClassVar = 2 z: ClassVar
If an assigned value is available (e.g. with
y
), the type should be inferred as some type to which this value is assignable (for example,int
,Literal[2]
, orAny
).If the bare
ClassVar
qualifier is used without any assigned value, the type should be inferred as Any. Type checkers may error if no assigned value is present.
Type annotations can be used to annotate class and instance variables
in class bodies and methods. In particular, the value-less notation a: int
allows one to annotate instance variables that should be initialized
in __init__
or __new__
. The syntax is as follows:
class BasicStarship:
captain: str = 'Picard' # instance variable with default
damage: int # instance variable without default
stats: ClassVar[dict[str, int]] = {} # class variable
Here ClassVar
is a special form defined by the typing
module that
indicates to the static type checker that this variable should not be
set on instances.
Note that a ClassVar
parameter cannot include any type variables, regardless
of the level of nesting: ClassVar[T]
and ClassVar[list[set[T]]]
are
both invalid if T
is a type variable.
This could be illustrated with a more detailed example. In this class:
class Starship:
captain = 'Picard'
stats = {}
def __init__(self, damage, captain=None):
self.damage = damage
if captain:
self.captain = captain # Else keep the default
def hit(self):
Starship.stats['hits'] = Starship.stats.get('hits', 0) + 1
stats
is intended to be a class variable (keeping track of many different
per-game statistics), while captain
is an instance variable with a default
value set in the class. This difference might not be seen by a type
checker: both get initialized in the class, but captain
serves only
as a convenient default value for the instance variable, while stats
is truly a class variable – it is intended to be shared by all instances.
Since both variables happen to be initialized at the class level, it is
useful to distinguish them by marking class variables as annotated with
types wrapped in ClassVar[...]
. In this way a type checker may flag
accidental assignments to attributes with the same name on instances.
For example, annotating the discussed class:
class Starship:
captain: str = 'Picard'
damage: int
stats: ClassVar[dict[str, int]] = {}
def __init__(self, damage: int, captain: str = None):
self.damage = damage
if captain:
self.captain = captain # Else keep the default
def hit(self):
Starship.stats['hits'] = Starship.stats.get('hits', 0) + 1
enterprise_d = Starship(3000)
enterprise_d.stats = {} # Flagged as error by a type checker
Starship.stats = {} # This is OK
As a matter of convenience (and convention), instance variables can be
annotated in __init__
or other methods, rather than in the class:
from typing import Generic, TypeVar
T = TypeVar('T')
class Box(Generic[T]):
def __init__(self, content):
self.content: T = content
ClassVar
cannot be used as a qualifier for a TypedDict
item or a NamedTuple field. Such usage also generates
an error at runtime.
@override
¶
(Originally specified by PEP 698.)
When type checkers encounter a method decorated with @typing.override
they
should treat it as a type error unless that method is overriding a method or
attribute in some ancestor class, and the type of the overriding method is
assignable to the type of the overridden method.
from typing import override
class Parent:
def foo(self) -> int:
return 1
def bar(self, x: str) -> str:
return x
class Child(Parent):
@override
def foo(self) -> int:
return 2
@override
def baz(self) -> int: # Type check error: no matching signature in ancestor
return 1
The @override
decorator should be permitted anywhere a type checker
considers a method to be a valid override, which typically includes not only
normal methods but also @property
, @staticmethod
, and @classmethod
.
Strict Enforcement Per-Project¶
We believe that @override
is most useful if checkers also allow developers
to opt into a strict mode where methods that override a parent class are
required to use the decorator. Strict enforcement should be opt-in for backward
compatibility.