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.
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:
final decoratorThe 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:
Final annotationThe currenttyping module lacks a way to indicate that a variablewill not be assigned to. This is a useful feature in severalsituations:
@property can make an attribute read-only butdoes not prevent overriding)NamedTuple, a tupleof types passed toisinstance, or an argument to a functionwith arguments ofLiteral type (PEP 586)).final decoratorThetyping.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.
Final annotationThetyping.Final type qualifier is used to indicate that avariable or attribute should not be reassigned, redefined, or overridden.
Final may be used in one of several forms:
Final[<type>]. Example:ID:Final[float]=1
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].
ID:Final[float]. If the right hand side is omitted, there mustbe an explicit type argument toFinal.self.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.The two main rules for defining a final name are:
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)])
The mypy[1] type checker supportsFinal andfinal. Areference implementation of the runtime component is provided in thetyping_extensions[2] module.
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
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