PEP: 591 Title: Adding a final qualifier to typing Author: Michael J.
Sullivan <sully@msully.net>, Ivan Levkivskyi <levkivskyi@gmail.com>
BDFL-Delegate: Guido van Rossum <guido@python.org> Discussions-To:
typing-sig@python.org Status: Final Type: Standards Track Topic: Typing
Created: 15-Mar-2019 Python-Version: 3.8 Post-History: Resolution:
https://mail.python.org/archives/list/typing-sig@python.org/message/FDO4KFYWYQEP3U2HVVBEBR3SXPHQSHYR/

typing:at-final/@typing.final <typing.final> and
typing:uppercase-final/typing.Final

Abstract

This PEP proposes a "final" qualifier to be added to the typing
module---in the form of a final decorator and a Final type
annotation---to serve three related purposes:

-   Declaring that a method should not be overridden
-   Declaring that a class should not be subclassed
-   Declaring that a variable or attribute should not be reassigned

Motivation

The final decorator

The current typing module lacks a way to restrict the use of inheritance
or overriding at a typechecker level. This is a common feature in other
object-oriented languages (such as Java), and is useful for reducing the
potential space of behaviors of a class, easing reasoning.

Some situations where a final class or method may be useful include:

-   A class wasn’t designed to be subclassed or a method wasn't designed
    to be overridden. Perhaps it would not work as expected, or be
    error-prone.
-   Subclassing or overriding 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.

The Final annotation

The current typing module lacks a way to indicate that a variable will
not be assigned to. This is a useful feature in several situations:

-   Preventing unintended modification of module and class level
    constants and documenting them as constants in a checkable way.
-   Creating a read-only attribute that may not be overridden by
    subclasses. (@property can make an attribute read-only but does not
    prevent overriding)
-   Allowing a name to be used in situations where ordinarily a literal
    is expected (for example as a field name for NamedTuple, a tuple of
    types passed to isinstance, or an argument to a function with
    arguments of Literal type (PEP 586)).

Specification

The final decorator

The typing.final decorator is used to restrict the use of inheritance
and overriding.

A type checker should prohibit any class decorated with @final from
being subclassed and any method decorated with @final from being
overridden in a subclass. The method decorator version may be used with
all of instance methods, class methods, static methods, and properties.

For example:

    from typing import final

    @final
    class Base:
        ...

    class Derived(Base):  # Error: Cannot inherit from final class "Base"
        ...

and:

    from typing import final

    class Base:
        @final
        def foo(self) -> None:
            ...

    class Derived(Base):
        def foo(self) -> None:  # Error: Cannot override final attribute "foo"
                                # (previously declared in base class "Base")
            ...

For overloaded methods, @final should be placed on the implementation
(or on the first overload, for 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):
            ...

It is an error to use @final on a non-method function.

The Final annotation

The typing.Final type qualifier is used to indicate that a variable or
attribute should not be reassigned, redefined, or overridden.

Syntax

Final may be used in one of several forms:

-   With an explicit type, using the syntax Final[<type>]. Example:

        ID: Final[float] = 1

-   With no type annotation. Example:

        ID: Final = 1

    The typechecker should apply its usual type inference mechanisms to
    determine the type of ID (here, likely, int). 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]. If the right hand side is omitted,
    there must be an explicit type argument to Final.

-   Finally, as 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.

Semantics and examples

The two main rules for defining a final name are:

-   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.

This means a type checker should prevent further assignments to final
names in type-checked code:

    from typing 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

Note that a type checker need not allow Final declarations inside loops
since the runtime will see multiple assignments to the same variable in
subsequent iterations.

Additionally, a type checker should prevent final 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

A final attribute declared in a class body without an initializer must
be initialized in the __init__ method (except 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

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:

    x: List[Final[int]] = []  # Error!

    def fun(x: Final[List[int]]) ->  None:  # Error!
        ...

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 with Final to
prevent mutating such values:

    x: Final = ['a', 'b']
    x.append('c')  # OK

    y: Final[Sequence[str]] = ['a', 'b']
    y.append('x')  # Error: "Sequence[str]" has no attribute "append"
    z: Final = ('a', 'b')  # Also works

Type checkers should treat uses of a final name that was initialized
with a literal as if it was replaced by the literal. For example, the
following should be allowed:

    from typing import NamedTuple, Final

    X: Final = "x"
    Y: Final = "y"
    N = NamedTuple("N", [(X, int), (Y, int)])

Reference Implementation

The mypy[1] type checker supports Final and final. A reference
implementation of the runtime component is provided in the
typing_extensions[2] module.

Rejected/deferred Ideas

The name Const was also considered as the name for the Final type
annotation. The name Final was chosen instead because the concepts are
related and it seemed best to be consistent between them.

We considered using a single name Final instead of introducing final as
well, but @Final just looked too weird to us.

A related feature to final classes would be Scala-style sealed classes,
where a class is allowed to be inherited only by classes defined in the
same module. Sealed classes seem most useful in combination with pattern
matching, so it does not seem to justify the complexity in our case.
This could be revisited in the future.

It would be possible to have the @final decorator on classes dynamically
prevent subclassing at runtime. Nothing else in typing does any runtime
enforcement, though, so final will not either. A workaround for when
both runtime enforcement and static checking is desired is to use this
idiom (possibly in a support module):

    if typing.TYPE_CHECKING:
        from typing import final
    else:
        from runtime_final import final

References

Copyright

This document has been placed in the public domain.

[1] http://www.mypy-lang.org/

[2] https://github.com/python/typing/tree/master/typing_extensions