Movatterモバイル変換


[0]ホーム

URL:


Following system colour schemeSelected dark colour schemeSelected light colour scheme

Python Enhancement Proposals

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

Important

This PEP is a historical document: see@final/@typing.finalandFinal/typing.Final for up-to-date specs and documentation. Canonical typing specs are maintained at thetyping specs site; runtime typing behaviour is described in the CPython documentation.

×

See thetyping specification update process for how to propose changes to the typing spec.

Abstract

This PEP proposes a “final” qualifier to be added to thetypingmodule—in the form of afinal decorator and aFinal typeannotation—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

Thefinal decorator

The currenttyping module lacks a way to restrict the use ofinheritance or overriding at a typechecker level. This is a commonfeature in other object-oriented languages (such as Java), and isuseful 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 designedto be overridden. Perhaps it would not work as expected, or beerror-prone.
  • Subclassing or overriding would make code harder to understand ormaintain. For example, you may want to prevent unnecessarily tightcoupling between base classes and subclasses.
  • You want to retain the freedom to arbitrarily change the classimplementation in the future, and these changes might breaksubclasses.

TheFinal annotation

The currenttyping module lacks a way to indicate that a variablewill not be assigned to. This is a useful feature in severalsituations:

  • Preventing unintended modification of module and class levelconstants and documenting them as constants in a checkable way.
  • Creating a read-only attribute that may not be overridden bysubclasses. (@property can make an attribute read-only butdoes not prevent overriding)
  • Allowing a name to be used in situations where ordinarily a literalis expected (for example as a field name forNamedTuple, a tupleof types passed toisinstance, or an argument to a functionwith arguments ofLiteral type (PEP 586)).

Specification

Thefinal decorator

Thetyping.final decorator is used to restrict the use ofinheritance and overriding.

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

For example:

fromtypingimportfinal@finalclassBase:...classDerived(Base):# Error: Cannot inherit from final class "Base"...

and:

fromtypingimportfinalclassBase:@finaldeffoo(self)->None:...classDerived(Base):deffoo(self)->None:# Error: Cannot override final attribute "foo"# (previously declared in base class "Base")...

For overloaded methods,@final should be placed on theimplementation (or on the first overload, for stubs):

fromtypingimportAny,overloadclassBase:@overloaddefmethod(self)->None:...@overloaddefmethod(self,arg:int)->int:...@finaldefmethod(self,x=None):...

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

TheFinal annotation

Thetyping.Final type qualifier is used to indicate that avariable 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 syntaxFinal[<type>]. Example:
    ID:Final[float]=1
  • With no type annotation. Example:
    ID:Final=1

    The typechecker should apply its usual type inference mechanisms todetermine the type ofID (here, likely,int). Note that unlike forgeneric classes this isnot the same asFinal[Any].

  • In class bodies and stub files you can omit the right hand side and just writeID:Final[float]. If the right hand side is omitted, there mustbe an explicit type argument toFinal.
  • Finally, asself.id:Final=1 (also optionally with a type insquare brackets). This is allowedonly in__init__ methods, sothat the final instance attribute is assigned only once when aninstance is created.

Semantics and examples

The two main rules for defining a final name are:

  • There can beat most one final declaration per module or class fora given attribute. There can’t be separate class-level and instance-levelconstants with the same name.
  • There must beexactly one assignment to a final name.

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

fromtypingimportFinalRATE:Final=3000classBase:DEFAULT_ID:Final=0RATE=300# Error: can't assign to final attributeBase.DEFAULT_ID=1# Error: can't override a final attribute

Note that a type checker need not allowFinal declarations inside loopssince the runtime will see multiple assignments to the same variable insubsequent iterations.

Additionally, a type checker should prevent final attributes frombeing overridden in a subclass:

fromtypingimportFinalclassWindow:BORDER_WIDTH:Final=2.5...classListView(Window):BORDER_WIDTH=3# Error: can't override a final attribute

A final attribute declared in a class body without an initializer mustbe initialized in the__init__ method (except in stub files):

classImmutablePoint:x:Final[int]y:Final[int]# Error: final attribute without an initializerdef__init__(self)->None:self.x=1# Good

Type checkers should infer a final attribute that is initialized ina class body as being a class variable. Variables should not be annotatedwith bothClassVar andFinal.

Final may only be used as the outermost type in assignments or variableannotations. 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!deffun(x:Final[List[int]])->None:# Error!...

Note that declaring a name as final only guarantees that the name willnot be re-bound to another value, but does not make the valueimmutable. Immutable ABCs and containers may be used in combinationwithFinal to prevent mutating such values:

x:Final=['a','b']x.append('c')# OKy: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 initializedwith a literal as if it was replaced by the literal. For example, thefollowing should be allowed:

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

Reference Implementation

The mypy[1] type checker supportsFinal andfinal. Areference implementation of the runtime component is provided in thetyping_extensions[2] module.

Rejected/deferred Ideas

The nameConst was also considered as the name for theFinaltype annotation. The nameFinal was chosen instead because theconcepts are related and it seemed best to be consistent between them.

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

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

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

iftyping.TYPE_CHECKING:fromtypingimportfinalelse:fromruntime_finalimportfinal

References

[1]
http://www.mypy-lang.org/
[2]
https://github.com/python/typing/tree/master/typing_extensions

Copyright

This document has been placed in the public domain.


Source:https://github.com/python/peps/blob/main/peps/pep-0591.rst

Last modified:2024-06-11 22:12:09 GMT


[8]ページ先頭

©2009-2025 Movatter.jp