Final names

You can use the typing_extensions.Final qualifier to indicate thata name or attribute should not be reassigned, redefined, oroverridden. This is often useful for module and class level constantsas a way to prevent unintended modification. Mypy will preventfurther assignments to final names in type-checked code:

  1. from typing_extensions import Final
  2.  
  3. RATE: Final = 3000
  4.  
  5. class Base:
  6. DEFAULT_ID: Final = 0
  7.  
  8. RATE = 300 # Error: can't assign to final attribute
  9. Base.DEFAULT_ID = 1 # Error: can't override a final attribute

Another use case for final attributes is to protect certain attributesfrom being overridden in a subclass:

  1. from typing_extensions import Final
  2.  
  3. class Window:
  4. BORDER_WIDTH: Final = 2.5
  5. ...
  6.  
  7. class ListView(Window):
  8. BORDER_WIDTH = 3 # Error: can't override a final attribute

You can use @property to make an attribute read-only, but unlike Final,it doesn’t work with module attributes, and it doesn’t prevent overriding insubclasses.

Syntax variants

You can use Final in one of these forms:

  • You can provide an explicit type using the syntax Final[<type>]. Example:
  1. ID: Final[float] = 1
  • You can omit the type:
  1. ID: Final = 1

Here mypy will infer type int for ID. Note that unlike forgeneric classes this is not the same as Final[Any].

  • In class bodies and stub files you can omit the right hand side and just writeID: Final[float].

  • Finally, you can write self.id: Final = 1 (also optionally witha type in square brackets). This is allowed only ininit methods, so that the final instance attribute isassigned only once when an instance is created.

Details of using Final

These are the two main rules for defining a final name:

  • There can be at 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 be exactly one assignment to a final name.

A final attribute declared in a class body without an initializer mustbe initialized in the init method (you can skip theinitializer in stub files):

  1. class ImmutablePoint:
  2. x: Final[int]
  3. y: Final[int] # Error: final attribute without an initializer
  4.  
  5. def __init__(self) -> None:
  6. self.x = 1 # Good

Final can 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:

  1. x: List[Final[int]] = [] # Error!
  2.  
  3. def fun(x: Final[List[int]]) -> None: # Error!
  4. ...

Final and ClassVar should not be used together. Mypy will inferthe scope of a final declaration automatically depending on whether it wasinitialized in the class body or in init.

A final attribute can’t be overridden by a subclass (even with anotherexplicit final declaration). Note however that a final attribute canoverride a read-only property:

  1. class Base:
  2. @property
  3. def ID(self) -> int: ...
  4.  
  5. class Derived(Base):
  6. ID: Final = 1 # OK

Declaring a name as final only guarantees that the name wll not be re-boundto another value. It doesn’t make the value immutable. You can use immutable ABCsand containers to prevent mutating such values:

  1. x: Final = ['a', 'b']
  2. x.append('c') # OK
  3.  
  4. y: Final[Sequence[str]] = ['a', 'b']
  5. y.append('x') # Error: Sequence is immutable
  6. z: Final = ('a', 'b') # Also an option