blob: 22010544ad1ab7be158f917fcd27f23d57ea9391 [file] [log] [blame]
.. _final_attrs:
Final names, methods and classes
================================
This section introduces these related features:
1. *Final names* are variables or attributes that should not be reassigned after
initialization. They are useful for declaring constants.
2. *Final methods* should not be overridden in a subclass.
3. *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.
.. note::
These are experimental features. They might change in later
versions of mypy. The *final* qualifiers are available through the
``typing_extensions`` package on PyPI.
Final names
-----------
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:
.. code-block:: python
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:
.. code-block:: python
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 :py:class:`@property <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:
.. code-block:: python
ID: Final[float] = 1
* You can omit the type:
.. code-block:: python
ID: Final = 1
Here mypy will infer type ``int`` for ``ID``. Note that unlike for
generic 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 with
a type in square brackets). This is allowed *only* in
:py:meth:`__init__ <object.__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 :py:meth:`__init__ <object.__init__>` method (you can skip the
initializer in stub files):
.. code-block:: python
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:
.. code-block:: python
x: List[Final[int]] = [] # Error!
def fun(x: Final[List[int]]) -> None: # Error!
...
``Final`` and :py:data:`~typing.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 :py:meth:`__init__ <object.__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:
.. code-block:: python
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:
.. code-block:: python
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_extensions.final``
decorator for this purpose:
.. code-block:: python
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
...
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):
.. code-block:: python
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_extensions.final`` decorator to a class to indicate
to mypy that it should not be subclassed:
.. code-block:: python
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.