|
| 1 | +Class basics |
| 2 | +============ |
| 3 | + |
| 4 | +Instance and class attributes |
| 5 | +***************************** |
| 6 | + |
| 7 | +Mypy type checker detects if you are trying to access a missing attribute, which is a very common programming error. For this to work correctly, instance and class attributes must be defined or initialized within the class. Mypy infers the types of attributes: |
| 8 | + |
| 9 | +.. code-block:: python |
| 10 | +
|
| 11 | + class A: |
| 12 | + def __init__(self, x: int) -> None: |
| 13 | + self.x = x # Attribute x of type int |
| 14 | +
|
| 15 | + a = A(1) |
| 16 | + a.x = 2 # OK |
| 17 | + a.y = 3 # Error: A has no attribute y |
| 18 | +
|
| 19 | +This is a bit like each class having an implicitly defined __slots__ attribute. In Python semantics this is only enforced during type checking: at runtime we use standard Python semantics. You can selectively define a class as *dynamic*; dynamic classes have Python-like compile-time semantics, and they allow you to assign to arbitrary attributes anywhere in a program without the type checker complaining: |
| 20 | + |
| 21 | +.. code-block:: python |
| 22 | +
|
| 23 | + from typing import Dynamic |
| 24 | +
|
| 25 | + class A(Dynamic): |
| 26 | + pass |
| 27 | +
|
| 28 | + a = A() |
| 29 | + a.x = 2 # OK, no need to define x explicitly. |
| 30 | +
|
| 31 | +Mypy also lets you read arbitrary attributes of dynamic class instances. This limits type checking effectiveness, so you should only use dynamic classes when you really need them. |
| 32 | + |
| 33 | +.. note:: |
| 34 | + |
| 35 | + Dynamic classes are not implemented in the current mypy version. |
| 36 | + |
| 37 | +You can declare variables in the class body explicitly using Undefined or a type comment: |
| 38 | + |
| 39 | +.. code-block:: python |
| 40 | +
|
| 41 | + class A: |
| 42 | + x = Undefined(List[int]) # Declare attribute y of type List[int] |
| 43 | + y = 0 # type: Any # Declare attribute x of type Any |
| 44 | +
|
| 45 | + a = A() |
| 46 | + a.x = [1] # OK |
| 47 | +
|
| 48 | +As in Python, a variable defined in the class body can used as a class or an instance variable. |
| 49 | + |
| 50 | +Similarly, you can give explicit types to instance variables defined in a method: |
| 51 | + |
| 52 | +.. code-block:: python |
| 53 | +
|
| 54 | + class A: |
| 55 | + def __init__(self) -> None: |
| 56 | + self.x = Undefined(List[int]) # OK |
| 57 | +
|
| 58 | + def f(self) -> None: |
| 59 | + self.y = 0 # type: Any # OK |
| 60 | +
|
| 61 | +You can only define an instance variable within a method if you assign to it explicitly using self: |
| 62 | + |
| 63 | +.. code-block:: python |
| 64 | +
|
| 65 | + class A: |
| 66 | + def __init__(self) -> None: |
| 67 | + self.y = 1 # Define y |
| 68 | + a = self |
| 69 | + a.x = 1 # Error: x not defined |
| 70 | +
|
| 71 | +Overriding statically typed methods |
| 72 | +*********************************** |
| 73 | + |
| 74 | +When overriding a statically typed method, mypy checks that the override has a compatible signature: |
| 75 | + |
| 76 | +.. code-block:: python |
| 77 | +
|
| 78 | + class A: |
| 79 | + def f(self, x: int) -> None: |
| 80 | + ... |
| 81 | +
|
| 82 | + class B(A): |
| 83 | + def f(self, x: str) -> None: # Error: type of x incompatible |
| 84 | + ... |
| 85 | +
|
| 86 | + class C(A): |
| 87 | + def f(self, x: int, y: int) -> None: # Error: too many arguments |
| 88 | + ... |
| 89 | +
|
| 90 | + class D(A): |
| 91 | + def f(self, x: int) -> None: # OK |
| 92 | + ... |
| 93 | +
|
| 94 | +.. note:: |
| 95 | + |
| 96 | + You can also vary return types **covariantly** in overriding. For example, you could override the return type 'object' with a subtype such as 'int'. |
| 97 | + |
| 98 | +You can also override a statically typed method with a dynamically typed one. This allows dynamically typed code to override methods defined in library classes without worrying about their type signatures, similar to Python. |
| 99 | + |
| 100 | +There is no runtime enforcement that the method override returns a value that is compatible with the original return type, since types are erased in the Python semantics: |
| 101 | + |
| 102 | +.. code-block:: python |
| 103 | +
|
| 104 | + class A: |
| 105 | + def inc(self, x: int) -> int: |
| 106 | + return x + 1 |
| 107 | +
|
| 108 | + class B(A): |
| 109 | + def inc(self, x): # Override, dynamically typed |
| 110 | + return 'hello' |
| 111 | +
|
| 112 | + b = B() |
| 113 | + print(b.inc(1)) # hello |
| 114 | + a = b # type: A |
| 115 | + print(a.inc(1)) # hello |
| 116 | +
|
| 117 | +Abstract base classes and multiple inheritance |
| 118 | +********************************************** |
| 119 | + |
| 120 | +Mypy uses Python abstract base classes for protocol types. There are several built-in abstract base classes types (for example, Sequence, Iterable and Iterator). You can define abstract base classes using the abc.ABCMeta metaclass and the abc.abstractmethod function decorator. |
| 121 | + |
| 122 | +.. code-block:: python |
| 123 | +
|
| 124 | + from abc import ABCMeta, abstractmethod |
| 125 | + import typing |
| 126 | +
|
| 127 | + class A(metaclass=ABCMeta): |
| 128 | + @abstractmethod |
| 129 | + def foo(self, x: int) -> None: pass |
| 130 | +
|
| 131 | + @abstractmethod |
| 132 | + def bar(self) -> str: pass |
| 133 | +
|
| 134 | + class B(A): |
| 135 | + def foo(self, x: int) -> None: ... |
| 136 | + def bar(self -> str: |
| 137 | + return 'x' |
| 138 | +
|
| 139 | + a = A() # Error: A is abstract |
| 140 | + b = B() # OK |
| 141 | +
|
| 142 | +Unlike most Python code, abstract base classes are likely to play a significant role in many complex mypy programs. |
| 143 | +
|
| 144 | +A class can inherit any number of classes, both abstract and concrete. As with normal overrides, a dynamically typed method can implement a statically typed abstract method defined in an abstract base class. |
| 145 | +
|
| 146 | +.. note:: |
| 147 | +
|
| 148 | + There are also plans to support more Python-style "duck typing" in the type system. The details are still open. |
0 commit comments