Final names, methods and classes¶
This section introduces these related features:
- Final names are variables or attributes that should not be reassigned after initialization. They are useful for declaring constants.
- Final methods should not be overridden in a subclass.
- Final classes should not be subclassed.
All of these are only enforced by mypy, and only in annotated code. They is no runtime enforcement by the Python runtime.
These are experimental features. They might change in later
versions of mypy. The final qualifiers are available through the
typing_extensions package on PyPI.
You can use the
typing_extensions.Final qualifier to indicate that
a name or attribute should not be reassigned, redefined, or
overridden. This is often useful for module and class level constants
as a way to prevent unintended modification. Mypy will prevent
further 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 attributes from 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
it doesn’t work with module attributes, and it doesn’t prevent overriding in
You can use
Final in one of these forms:
You can provide an explicit type using the syntax
ID: Final[float] = 1
You can omit the type:
ID: Final = 1
Here mypy will infer type
ID. Note that unlike for generic classes this is not the same as
In class bodies and stub files you can omit the right hand side and just write
Finally, you can write
self.id: Final = 1(also optionally with a type in square brackets). This is allowed only in
__init__methods, so that the final instance attribute is assigned only once when an instance is created.
Details of using
These are the two main rules for defining a final name:
- There can be at most one final declaration per module or class for a given attribute. There can’t be separate class-level and instance-level constants 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 must
be initialized in the
__init__ method (you can skip the
initializer 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 variable
annotations. 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! ...
A final attribute can’t be overridden by a subclass (even with another explicit final declaration). Note however that a final attribute can override 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-bound to another value. It doesn’t make the value immutable. You can use immutable ABCs and 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
Like with attributes, sometimes it is useful to protect a method from
overriding. You can use the
decorator for this purpose:
from typing_extensions import final class Base: @final def common_name(self) -> None: ... class Derived(Base): def common_name(self) -> None: # Error: cannot override a final method ...
@final decorator can be used with instance methods, class methods,
static methods, and properties.
For overloaded methods you should add
@final on the implementation
to make it final (or on the first overload in stubs):
from typing import Any, overload class Base: @overload def method(self) -> None: ... @overload def method(self, arg: int) -> int: ... @final def method(self, x=None): ...
You can apply the
typing_extensions.final decorator to a class to indicate
to mypy that it should not be subclassed:
from typing_extensions import final @final class Leaf: ... class MyLeaf(Leaf): # Error: Leaf can't be subclassed ...
The decorator acts as a declaration for mypy (and as documentation for humans), but it doesn’t actually prevent subclassing at runtime.
Here are some situations where using a final class may be useful:
- A class wasn’t designed to be subclassed. Perhaps subclassing would not work as expected, or subclassing would be error-prone.
- Subclassing would make code harder to understand or maintain. For example, you may want to prevent unnecessarily tight coupling between base classes and subclasses.
- You want to retain the freedom to arbitrarily change the class implementation in the future, and these changes might break subclasses.
An abstract class that defines at least one abstract method or
property and has
@final decorator will generate an error from
mypy, since those attributes could never be implemented.
from abc import ABCMeta, abstractmethod from typing_extensions import final @final class A(metaclass=ABCMeta): # error: Final class A has abstract attributes "f" @abstractmethod def f(self, x: int) -> None: pass