File: final_attrs.rst

package info (click to toggle)
mypy 1.15.0-5
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 20,576 kB
  • sloc: python: 105,159; cpp: 11,380; ansic: 6,629; makefile: 247; sh: 20
file content (237 lines) | stat: -rw-r--r-- 7,038 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
.. _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.
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 to prevent unintended modification. Mypy will prevent
further assignments to final names in type-checked code:

.. code-block:: python

   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:

.. code-block:: python

   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 :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[int] = 1

  Here, mypy will infer type ``int`` for ``ID``.

* You can omit the type:

  .. code-block:: python

     ID: Final = 1

  Here, mypy will infer type ``Literal[1]`` 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[int]``.

* 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 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 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:

.. 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.final`` decorator for this purpose:

.. code-block:: python

   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):

.. code-block:: python

   from typing import final, 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:

.. code-block:: python

   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.

.. code-block:: python

    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