Final names
You can use the typing_extensions.Final
qualifier to indicate thata name or attribute should not be reassigned, redefined, oroverridden. This is often useful for module and class level constantsas a way to prevent unintended modification. Mypy will preventfurther assignments to final names in type-checked code:
- from typing_extensions import Final
- RATE: Final = 3000
- class Base:
- DEFAULT_ID: Final = 0
- RATE = 300 # Error: can't assign to final attribute
- Base.DEFAULT_ID = 1 # Error: can't override a final attribute
Another use case for final attributes is to protect certain attributesfrom being overridden in a subclass:
- from typing_extensions import Final
- class Window:
- BORDER_WIDTH: Final = 2.5
- ...
- class ListView(Window):
- BORDER_WIDTH = 3 # Error: can't override a final attribute
You can use @property
to make an attribute read-only, but unlike Final
,it doesn’t work with module attributes, and it doesn’t prevent overriding insubclasses.
Syntax variants
You can use Final
in one of these forms:
- You can provide an explicit type using the syntax
Final[<type>]
. Example:
- ID: Final[float] = 1
- You can omit the type:
- ID: Final = 1
Here mypy will infer type int
for ID
. Note that unlike forgeneric classes this is not the same as Final[Any]
.
In class bodies and stub files you can omit the right hand side and just write
ID: Final[float]
.Finally, you can write
self.id: Final = 1
(also optionally witha type in square brackets). This is allowed only ininit
methods, so that the final instance attribute isassigned only once when an instance is created.
Details of using Final
These are the two main rules for defining a final name:
- There can be at most one final declaration per module or class fora given attribute. There can’t be separate class-level and instance-levelconstants with the same name.
- There must be exactly one assignment to a final name.
A final attribute declared in a class body without an initializer mustbe initialized in the init
method (you can skip theinitializer in stub files):
- class ImmutablePoint:
- x: Final[int]
- y: Final[int] # Error: final attribute without an initializer
- def __init__(self) -> None:
- self.x = 1 # Good
Final
can only be used as the outermost type in assignments or variableannotations. Using it in any other position is an error. In particular,Final
can’t be used in annotations for function arguments:
- x: List[Final[int]] = [] # Error!
- def fun(x: Final[List[int]]) -> None: # Error!
- ...
Final
and ClassVar
should not be used together. Mypy will inferthe scope of a final declaration automatically depending on whether it wasinitialized in the class body or in init
.
A final attribute can’t be overridden by a subclass (even with anotherexplicit final declaration). Note however that a final attribute canoverride a read-only property:
- class Base:
- @property
- def ID(self) -> int: ...
- class Derived(Base):
- ID: Final = 1 # OK
Declaring a name as final only guarantees that the name wll not be re-boundto another value. It doesn’t make the value immutable. You can use immutable ABCsand containers to prevent mutating such values:
- x: Final = ['a', 'b']
- x.append('c') # OK
- y: Final[Sequence[str]] = ['a', 'b']
- y.append('x') # Error: Sequence is immutable
- z: Final = ('a', 'b') # Also an option