Type qualifiers — Mypy 0.630+dev-11a157ecf60ba11d7f82a7409d22a5f3fe6f5c81-dirty documentation

# Type qualifiers

This section describes constructs that do not affect types of variables and methods, but affect how they can be accessed, assigned, and overridden.

## Class and instance variables

By default mypy assumes that a variable declared in the class body is an instance variable. One can mark names intended to be used as class variables with a special type qualifier `ClassVar`. For example:

    from typing import ClassVar

    class Base:
        attr: int  # This is an instance variable
        num_subclasses: ClassVar[int]  # This is a class variable

        def foo(self) -> None:
            self.attr = 0  # OK
            self.num_subclasses = 0  # Error: Cannot assign to class variable via instance

    Base.num_subclasses = 0  # OK
    Base.attr = 0  # Also OK, sets default value for an instance variable

Note that `ClassVar` is not valid as a nested type, and in any position other than assignment in class body. For example:

    x: ClassVar[int]  # Error: ClassVar can't be used at module scope

    class C:
        y: List[ClassVar[int]]  # Error: can't use ClassVar as nested type

## Final attributes of classes and modules

Note

This is an experimental feature. Some details might change in later versions of mypy. The final qualifiers are available in `typing_extensions` module. When the semantics is stable, they will be added to `typing`.

There are several situations where static guarantees about non-redefinition of certain names (or references in general) can be useful. One such example is module or class level constants, user might want to guard them against unintentional modifications:

    RATE = 3000

    class Base:
        DEFAULT_ID = 0

    # 1000 lines later

    class Derived(Base):
        DEFAULT_ID = 1  # this may be unintentional

    RATE = 300  # this too

Another example is where a user might want to protect certain instance attributes from overriding in a subclass:

    import uuid

    class Snowflake:
        """An absolutely unique object in the database"""
        def __init__(self) -> None:
            self.id = uuid.uuid4()

    # 1000 lines later

    class User(Snowflake):
        id = uuid.uuid4()  # This has valid type, but the meaning
                           # may be wrong

Some other use cases might be solved by using `@property`, but note that both above use cases can’t be solved this way. For such situations, one might want to use `typing.Final`.

### Definition syntax

The `typing.Final` type qualifier indicates that a given name or attribute should never be re-assigned, re-defined, nor overridden. It can be used in one of these forms:

- The simplest one is `ID: Final = 1`. Note that unlike gor generic classes this is not the same as `Final[Any]`. Here mypy will infer type `int`.
- An explicit type `ID: Final[float] = 1` can be used as in any normal assignment.
- In stub files one can omit the right hand side and just write `ID: Final[float]`.
- Finally, one can define `self.id: Final = 1` (also with a type argument), but this is allowed only in `__init__` methods.

### Definition rules

The are two rules that should be always followed when defining a final name:

- There can be at most one final declaration per module or class for a given attribute:

      ID: Final = 1
      ID: Final = 2  # Error!

      class SomeCls:
          id: Final = 1
          def __init__(self, x: int) -> None:
              self.id: Final = x  # Error!

  Note that mypy has a single namespace for a class. So there can’t be two class-level and instance-level constants with the same name.

- There must be exactly one assignment to a final attribute:

      ID = 1
      ID: Final = 2  # Error!

      class SomeCls:
          ID = 1
          ID: Final = 2  # Error!

Note

Conditional final declarations and final declarations within loops are not supported.

### Using final attributes

As a result of a final declaration mypy strives to provide the two following guarantees:

- A final attribute can’t be re-assigned (or otherwise re-defined), both internally and externally:

      # file mod.py
      from typing import Final

      ID: Final = 1

      # file main.py
      from typing import Final

      import mod
      mod.ID = 2  # Error, can't assign to constant.

      class SomeCls:
          ID: Final = 1

          def meth(self) -> None:
              self.ID = 2  # Error, can't assign to final attribute

      class DerivedCls(SomeCls):
          ...

      DerivedCls.ID = 2  # Error!
      obj: DerivedCls
      obj.ID = 2  # Error!

- A final attribute can’t be overridden by a subclass (even with another explicit final declaration). Note however, that final attributes can override normal attributes. This also applies to multiple inheritance:

      class Base:
          ID = 0

      class One(Base):
          ID: Final = 1  # OK

      class Other(Base):
          ID: Final = 2  # OK

      class Combo(One, Other):  # Error, cannot override final attribute.
          pass

Note

Mypy treats re-exported final names as final. In other words, once declared, the final status can’t be “stripped”. Such behaviour is typically desired for larger libraries where constants are defined in a separate module and then re-exported.

### Final methods

Like with attributes, sometimes it is useful to protect a method from overriding. In such situations one can use a `typing.final` decorator:

    from typing import final

    class Base:
        @final
        def common_name(self) -> None:  # common signature
            ...

    # 1000 lines later

    class Derived(Base):
        def common_name(self) -> None:  # Error, this overriding might break
                                        # invariants in the base class.
            ...

This `@final` decorator can be used with instance methods, class methods, static methods, and properties (this includes overloaded methods).

### Final classes

As a bonus, applying a `typing.final` decorator to a class indicates to mypy that it can’t be subclassed. Mypy doesn’t provide any additional features for final classes, but some other tools may use this information for their benefits. Plus it serves a verifiable documentation purpose:

    # file lib.pyi
    from typing import final

    @final
    class Leaf:
        ...

    # file main.py
    from lib import Leaf

    class MyLeaf(Leaf):  # Error, library author believes this is unsafe
        ...