Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

spec: clarify interaction of Final and dataclass #1669

Merged
merged 2 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
carljm marked this conversation as resolved.
Show resolved Hide resolved
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