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. There is no runtime enforcement by the Python runtime.
Note
The examples in this page import Final
and final
from the
typing
module. These types were added to typing
in Python 3.8,
but are also available for use in Python 3.4 - 3.7 via the
typing_extensions
package.
Final names#
You can use the typing.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 import Final
RATE: Final = 3_000
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 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 in
subclasses.
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[int] = 1
Here mypy will infer type
int
forID
.You can omit the type:
ID: Final = 1
Here mypy will infer type
Literal[1]
forID
. Note that unlike for generic classes this is not the same asFinal[Any]
.In class bodies and stub files you can omit the right hand side and just write
ID: Final[int]
.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 Final
#
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!
...
Final
and ClassVar
should not be used together. Mypy will infer
the scope of a final declaration automatically depending on whether it was
initialized in the class body or in __init__
.
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 will 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
Final methods#
Like with attributes, sometimes it is useful to protect a method from
overriding. You can use the typing.final
decorator for this purpose:
from typing import final
class Base:
@final
def common_name(self) -> None:
...
class Derived(Base):
def common_name(self) -> None: # Error: cannot override a final method
...
This @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):
...
Final classes#
You can apply the typing.final
decorator to a class to indicate
to mypy that it should not be subclassed:
from typing 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 import final
@final
class A(metaclass=ABCMeta): # error: Final class A has abstract attributes "f"
@abstractmethod
def f(self, x: int) -> None: pass