TL; DR : if you use Python 4.0, it just works. From today (2019) to 3. 7+, you should enable this function using the operator of the future ( from __future__ import annotations ) - for Python 3.6 or below, use the line.
I think you got this exception:
NameError: name 'Position' is not defined
This is because Position must be defined before you can use it in the annotation if you are not using Python 4.
Python 3. 7+: from __future__ import annotations
Python 3.7 introduces PEP 563: deferred annotation score . A module that uses the future from __future__ import annotations will automatically save annotations as strings:
from __future__ import annotations class Position: def __add__(self, other: Position) -> Position: ...
It is planned to become the default in Python 4.0. Since Python is still a dynamic typing language, so type checking at runtime fails, annotation input should not affect performance, right? Wrong! Prior to Python 3.7, the input module was one of the slowest Python modules in the kernel, so if you import typing you will see a performance increase of up to 7 times when upgrading to 3.7.
Python <3.7: use string
According to PEP 484 , you should use a string instead of the class itself:
class Position: ... def __add__(self, other: 'Position') -> 'Position': ...
If you are using the Django framework, this may be familiar, as Django models also use strings for direct references (defining a foreign key where the foreign model is self or not yet declared). This should work with Pycharm and other tools.
sources
Relevant parts of PEP 484 and PEP 563 to save you from traveling:
Direct links
When a type hint contains names that are not yet defined, this definition can be expressed as a string literal, which will be resolved later.
The situation in which this usually happens is the definition of the container class, where the class being defined is found in the signature of some methods. For example, the following code (beginning to implement a simple binary tree) does not work:
class Tree: def __init__(self, left: Tree, right: Tree): self.left = left self.right = right
To solve this problem, we write:
class Tree: def __init__(self, left: 'Tree', right: 'Tree'): self.left = left self.right = right
The string literal must contain a valid Python expression (i.e., Compile (lit, '', 'eval') must be a valid code object), and it must be evaluated without errors after the module is fully loaded. The local and global namespaces in which it is evaluated must be the same namespaces in which the default arguments for the same function will be evaluated.
and PEP 563:
In Python 4.0, annotations of functions and variables will no longer be evaluated at definition time. Instead, the string form will be stored in the corresponding __annotations__ dictionary. Static type validators will not see a difference in behavior, while tools that use annotations at runtime will have to defer evaluation.
...
The functions described above can be enabled starting with Python 3.7 using the following special import:
from __future__ import annotations
Things you can experience instead
A. Define Fictitious Position
Before defining a class, place a dummy definition:
class Position(object): pass class Position(object): ...
This will NameError and may even look good:
>>> Position.__add__.__annotations__ {'other': __main__.Position, 'return': __main__.Position}
But is it?
>>> for k, v in Position.__add__.__annotations__.items(): ... print(k, 'is Position:', v is Position) return is Position: False other is Position: False
B. Monkey patch for adding annotations:
You might want to try the magic of metaprogramming in Python and write a decorator that will correct the class definition to add annotations:
class Position: ... def __add__(self, other): return self.__class__(self.x + other.x, self.y + other.y)
The decorator should be responsible for the equivalent of this:
Position.__add__.__annotations__['return'] = Position Position.__add__.__annotations__['other'] = Position
At least that seems correct:
>>> for k, v in Position.__add__.__annotations__.items(): ... print(k, 'is Position:', v is Position) return is Position: True other is Position: True
There are probably too many problems.
Conclusion
If you use 3.6 or lower, use a string literal containing the class name, in 3.7 use from __future__ import annotations and this will just work.