A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://peps.python.org/pep-0591/ below:

PEP 591 – Adding a final qualifier to typing

PEP 591 – Adding a final qualifier to typing
Author:
Michael J. Sullivan <sully at msully.net>, Ivan Levkivskyi <levkivskyi at gmail.com>
BDFL-Delegate:
Guido van Rossum <guido at python.org>
Discussions-To:
Typing-SIG list
Status:
Final
Type:
Standards Track
Topic:
Typing
Created:
15-Mar-2019
Python-Version:
3.8
Post-History:
Resolution:
Typing-SIG message
Table of Contents 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:

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:

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:

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:

Semantics and examples

The two main rules for defining a final name are:

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.


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4