I have the following code in python 3:
class Position:
def __init__(self, x: int, y: int):
self.x = x
self.y = y
def __add__(self, other: Position) -> Position:
return Position(self.x + other.x, self.y + other.y)
But my editor (PyCharm) says that the reference Position can not be resolved (in the _add__ method). How should I specify that I expect the return type to be of type Position
?
Edit: I think this is actually a PyCharm issue. It actually uses the information in its warnings, and code completion
But correct me if I'm wrong, and need to use some other syntax.
TL;DR: if you are using Python 3.7 or above import the
annotations
module and it will work as you expect - for Python 3.6 or below use a string.I guess you got this exception:
This is because
Position
must be defined before you can use it in an annotation unless you are using Python 4.Python 3.7+:
from __future__ import annotations
Python 3.7 introduces PEP 563: postponed evaluation of annotations. A module that uses the future statement
from __future__ import annotations
will store annotations as strings automatically:This is scheduled to become the default in Python 4.0. Since Python still is a dynamically typed language so no type checking is done at runtime, typing annotations should have no performance impact, right? Wrong! Before python 3.7 the typing module used to be one of the slowest python modules in core so if you
import typing
you will see up to 7 times increase in performance when you upgrade to 3.7.Python <3.7: use a string
According to PEP 484, you should use a string instead of the class itself:
If you use the Django framework this may be familiar as Django models also use strings for forward references (foreign key definitions where the foreign model is
self
or is not declared yet). This should work with Pycharm and other tools.Sources
The relevant parts of PEP 484 and PEP 563, to spare you the trip:
and PEP 563:
Things that you may be tempted to do instead
A. Define a dummy
Position
Before the class definition, place a dummy definition:
This will get rid of the
NameError
and may even look OK:But is it?
B. Monkey-patch in order to add the annotations:
You may want to try some Python meta programming magic and write a decorator to monkey-patch the class definition in order to add annotations:
The decorator should be responsible for the equivalent of this:
At least it seems right:
Probably too much trouble.
Conclusion
If you are using 3.6 or below use a string literal containing the class name, in 3.7 use
from __future__ import annotations
and it will just work.Specifying the type as string is fine, but always grates me a bit that we are basically circumventing the parser. So you better not misspell any one of these literal strings:
A slight variation is to use a bound typevar, at least then you have to write the string only once when declaring the typevar:
The name 'Position' is not avalilable at the time the class body itself is parsed. I don't know how you are using the type declarations, but Python's PEP 484 - which is what most mode should use if using these typing hints say that you can simply put the name as a string at this point:
Check https://www.python.org/dev/peps/pep-0484/#forward-references - tools conforming to that will know to unwrap the class name from there and make use of it.(It is always important to have in mind that the Python language itself does nothing of these annotations - they are usually meant for static-code analysis, or one could have a library/framework for type checking in run-time - but you have to explicitly set that)