Skip to content

Commit

Permalink
spec: clarify interaction of Final and dataclass (#1669)
Browse files Browse the repository at this point in the history
  • Loading branch information
carljm committed Apr 11, 2024
1 parent 0256c38 commit 7a70cd6
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 7 deletions.
10 changes: 10 additions & 0 deletions docs/spec/dataclasses.rst
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,16 @@ This includes, but is not limited to, the following semantics:
* ClassVar attributes are not considered dataclass fields and are
`ignored by dataclass mechanisms <https://docs.python.org/3/library/dataclasses.html#class-variables>`_.

* A dataclass field may be annotated with ``Final[...]``. For example, ``x:
Final[int]`` in a dataclass body specifies a dataclass field ``x``, which
will be initialized in the generated ``__init__`` and cannot be assigned to
thereafter. A ``Final`` dataclass field initialized in the class body is not
a class attribute unless explicitly annotated with ``ClassVar``. For example,
``x: Final[int] = 3`` is a dataclass field named ``x`` with a default value
of ``3`` in the generated ``__init__`` method. A final class variable on a
dataclass must be explicitly annotated as e.g. ``x: ClassVar[Final[int]] =
3``.


Undefined behavior
^^^^^^^^^^^^^^^^^^
Expand Down
26 changes: 19 additions & 7 deletions docs/spec/qualifiers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -147,19 +147,31 @@ be initialized in the ``__init__`` method (except in stub files)::
def __init__(self) -> None:
self.x = 1 # Good

Type checkers should infer a final attribute that is initialized in
a class body as being a class variable. Variables should not be annotated
with both ``ClassVar`` and ``Final``.

``Final`` may 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::
The generated ``__init__`` method of :doc:`dataclasses` qualifies for this
requirement: a bare ``x: Final[int]`` is permitted in a dataclass body, because
the generated ``__init__`` will initialize ``x``.

Type checkers should infer a final attribute that is initialized in a class
body as being a class variable, except in the case of :doc:`dataclasses`, where
``x: Final[int] = 3`` creates a dataclass field and instance-level final
attribute ``x`` with default value ``3``; ``x: ClassVar[Final[int]] = 3`` is
necessary to create a final class variable with value ``3``. In
non-dataclasses, combining ``ClassVar`` and ``Final`` is redundant, and type
checkers may choose to warn or error on the redundancy.

``Final`` may only be used 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`` may be wrapped only by other type qualifiers (e.g. ``ClassVar`` or
``Annotation``). It cannot be used in a type parameter (e.g.
``list[Final[int]]`` is not permitted.)

Note that declaring a name as final only guarantees that the name will
not be re-bound to another value, but does not make the value
immutable. Immutable ABCs and containers may be used in combination
Expand Down

0 comments on commit 7a70cd6

Please sign in to comment.