I guess you got this exception:
NameError: name 'Position' is not defined
This is because in the original implementation of annotations, Position must be defined before you can use it in an annotation.
Python 3.14+: It'll just work
Python 3.14 has a new, lazily evaluated annotation implementation specified by PEP 749 and 649. Annotations will be compiled to special __annotate__ functions, executed when an object's __annotations__ dict is first accessed instead of at the point where the annotation itself occurs.
Thus, annotating your function as def __add__(self, other: Position) -> Position: no longer requires Position to already exist:
class Position:
def __add__(self, other: Position) -> Position:
...
Python 3.7+, deprecated: from __future__ import annotations
from __future__ import annotations turns on an older solution to this problem, PEP 563, where all annotations are saved as strings instead of as __annotate__ functions or evaluated values. This was originally planned to become the default behavior, and almost became the default in 3.10 before being reverted.
With the acceptance of PEP 749, this will be deprecated in Python 3.14, and it will be removed in a future Python version. Still, it works for now:
from __future__ import annotations
class Position:
def __add__(self, other: Position) -> Position:
...
Python 3+: Use a string
This is the original workaround, specified in PEP 484. Write your annotations as string literals containing the text of whatever expression you originally wanted to use as an annotation:
class Position:
def __add__(self, other: 'Position') -> 'Position':
...
from __future__ import annotations effectively automates doing this for all annotations in a file.
typing.Self might sometimes be appropriate
Introduced in Python 3.11, typing.Self refers to the type of the current instance, even if that type is a subclass of the class the annotation appears in. So if you have the following code:
from typing import Self
class Parent:
def me(self) -> Self:
return self
class Child(Parent): pass
x: Child = Child().me()
then Child().me() is treated as returning Child, instead of Parent.
This isn't always what you want. But when it is, it's pretty convenient.
For Python versions < 3.11, if you have typing_extensions installed, you can use:
from typing_extensions import Self
Sources
The relevant parts of PEP 484, PEP 563, and PEP 649, to spare you the trip:
Forward references
When a type hint contains names that have not been defined yet, that definition may be expressed as a string literal, to be resolved later.
A situation where this occurs commonly is the definition of a container class, where the class being defined occurs in the signature of some of the methods. For example, the following code (the start of a simple binary tree implementation) does not work:
class Tree: def __init__(self, left: Tree, right: Tree): self.left = left self.right = rightTo address this, we write:
class Tree: def __init__(self, left: 'Tree', right: 'Tree'): self.left = left self.right = rightThe string literal should contain a valid Python expression (i.e., compile(lit, '', 'eval') should be a valid code object) and it should evaluate without errors once the module has been fully loaded. The local and global namespace in which it is evaluated should be the same namespaces in which default arguments to the same function would be evaluated.
and PEP 563, deprecated:
Implementation
In Python 3.10, function and variable annotations will no longer be evaluated at definition time. Instead, a string form will be preserved in the respective
__annotations__dictionary. Static type checkers will see no difference in behavior, whereas tools using annotations at runtime will have to perform postponed evaluation....
Enabling the future behavior in Python 3.7
The functionality described above can be enabled starting from Python 3.7 using the following special import:
from __future__ import annotations
and PEP 649:
Overview
This PEP adds a new dunder attribute to the objects that support annotations–functions, classes, and modules. The new attribute is called
__annotate__, and is a reference to a function which computes and returns that object’s annotations dict.At compile time, if the definition of an object includes annotations, the Python compiler will write the expressions computing the annotations into its own function. When run, the function will return the annotations dict. The Python compiler then stores a reference to this function in
__annotate__on the object.Furthermore,
__annotations__is redefined to be a “data descriptor” which calls this annotation function once and caches the result.
Things that you may be tempted to do instead
A. Define a dummy Position
Before the class definition, place a dummy definition:
class Position(object):
pass
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)
This will get rid of the NameError and may even look OK:
>>> 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
And mypy will report a pile of errors:
main.py:4: error: Name "Position" already defined on line 1 [no-redef]
main.py:11: error: Too many arguments for "Position" [call-arg]
main.py:11: error: "Position" has no attribute "x" [attr-defined]
main.py:11: error: "Position" has no attribute "y" [attr-defined]
Found 4 errors in 1 file (checked 1 source file)
B. Monkey-patch in order to add the annotations:
You may want to try some Python metaprogramming magic and write a decorator to monkey-patch the class definition in order 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
It'll work right at runtime:
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: True
other is Position: True
But static analyzers like mypy won't understand it, and static analysis is the biggest use case of type annotations.
Answer from Paulo Scardine on Stack OverflowI guess you got this exception:
NameError: name 'Position' is not defined
This is because in the original implementation of annotations, Position must be defined before you can use it in an annotation.
Python 3.14+: It'll just work
Python 3.14 has a new, lazily evaluated annotation implementation specified by PEP 749 and 649. Annotations will be compiled to special __annotate__ functions, executed when an object's __annotations__ dict is first accessed instead of at the point where the annotation itself occurs.
Thus, annotating your function as def __add__(self, other: Position) -> Position: no longer requires Position to already exist:
class Position:
def __add__(self, other: Position) -> Position:
...
Python 3.7+, deprecated: from __future__ import annotations
from __future__ import annotations turns on an older solution to this problem, PEP 563, where all annotations are saved as strings instead of as __annotate__ functions or evaluated values. This was originally planned to become the default behavior, and almost became the default in 3.10 before being reverted.
With the acceptance of PEP 749, this will be deprecated in Python 3.14, and it will be removed in a future Python version. Still, it works for now:
from __future__ import annotations
class Position:
def __add__(self, other: Position) -> Position:
...
Python 3+: Use a string
This is the original workaround, specified in PEP 484. Write your annotations as string literals containing the text of whatever expression you originally wanted to use as an annotation:
class Position:
def __add__(self, other: 'Position') -> 'Position':
...
from __future__ import annotations effectively automates doing this for all annotations in a file.
typing.Self might sometimes be appropriate
Introduced in Python 3.11, typing.Self refers to the type of the current instance, even if that type is a subclass of the class the annotation appears in. So if you have the following code:
from typing import Self
class Parent:
def me(self) -> Self:
return self
class Child(Parent): pass
x: Child = Child().me()
then Child().me() is treated as returning Child, instead of Parent.
This isn't always what you want. But when it is, it's pretty convenient.
For Python versions < 3.11, if you have typing_extensions installed, you can use:
from typing_extensions import Self
Sources
The relevant parts of PEP 484, PEP 563, and PEP 649, to spare you the trip:
Forward references
When a type hint contains names that have not been defined yet, that definition may be expressed as a string literal, to be resolved later.
A situation where this occurs commonly is the definition of a container class, where the class being defined occurs in the signature of some of the methods. For example, the following code (the start of a simple binary tree implementation) does not work:
class Tree: def __init__(self, left: Tree, right: Tree): self.left = left self.right = rightTo address this, we write:
class Tree: def __init__(self, left: 'Tree', right: 'Tree'): self.left = left self.right = rightThe string literal should contain a valid Python expression (i.e., compile(lit, '', 'eval') should be a valid code object) and it should evaluate without errors once the module has been fully loaded. The local and global namespace in which it is evaluated should be the same namespaces in which default arguments to the same function would be evaluated.
and PEP 563, deprecated:
Implementation
In Python 3.10, function and variable annotations will no longer be evaluated at definition time. Instead, a string form will be preserved in the respective
__annotations__dictionary. Static type checkers will see no difference in behavior, whereas tools using annotations at runtime will have to perform postponed evaluation....
Enabling the future behavior in Python 3.7
The functionality described above can be enabled starting from Python 3.7 using the following special import:
from __future__ import annotations
and PEP 649:
Overview
This PEP adds a new dunder attribute to the objects that support annotations–functions, classes, and modules. The new attribute is called
__annotate__, and is a reference to a function which computes and returns that object’s annotations dict.At compile time, if the definition of an object includes annotations, the Python compiler will write the expressions computing the annotations into its own function. When run, the function will return the annotations dict. The Python compiler then stores a reference to this function in
__annotate__on the object.Furthermore,
__annotations__is redefined to be a “data descriptor” which calls this annotation function once and caches the result.
Things that you may be tempted to do instead
A. Define a dummy Position
Before the class definition, place a dummy definition:
class Position(object):
pass
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)
This will get rid of the NameError and may even look OK:
>>> 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
And mypy will report a pile of errors:
main.py:4: error: Name "Position" already defined on line 1 [no-redef]
main.py:11: error: Too many arguments for "Position" [call-arg]
main.py:11: error: "Position" has no attribute "x" [attr-defined]
main.py:11: error: "Position" has no attribute "y" [attr-defined]
Found 4 errors in 1 file (checked 1 source file)
B. Monkey-patch in order to add the annotations:
You may want to try some Python metaprogramming magic and write a decorator to monkey-patch the class definition in order 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
It'll work right at runtime:
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: True
other is Position: True
But static analyzers like mypy won't understand it, and static analysis is the biggest use case of type annotations.
PEP 673 which is implemented in Python 3.11, adds the Self type.
from typing import Self
class Position:
def __init__(self, x: int, y: int):
self.x = x
self.y = y
def __add__(self, other: Self) -> Self:
return type(self)(self.x + other.x, self.y + other.y)
Returning Self is often a good idea, but you must return an object of the same type as self, which means calling type(self) rather than Position.
For older versions of Python (currently 3.7 and later), use the typing-extensions package. One of its purposes is to
Enable use of new type system features on older Python versions. For example,
typing.TypeGuardis new in Python 3.10, buttyping_extensionsallows users on previous Python versions to use it too.
Then you just import from typing_extensions instead of typing, e.g. from typing_extensions import Self.
Videos
I have two custom classes, one of which requires the other as an input argument.
class Authentication(object):
def __init__(self) -> None:
self.auth_data = self._resolve_auth()
class Data(object):
def __init__(self, authentication) -> None:
self.baseurl = authentication.baseurl
self.token = authentication.tokenI have the Authentication class defined in a seperate file in my project. So my question is, if I want to apply type-hinting to the Data class i.e.
def __init__(self, authentication: Authentication) -> None:
How do I go about doing this without having to import the Authentication class into my file which contains the Data class? It seems like overkill just to get proper type-hinting.
The former is correct, if arg accepts an instance of CustomClass:
def FuncA(arg: CustomClass):
# ^ instance of CustomClass
In case you want the class CustomClass itself (or a subtype), then you should write:
from typing import Type # you have to import Type
def FuncA(arg: Type[CustomClass]):
# ^ CustomClass (class object) itself
Like it is written in the documentation about Typing:
class typing.Type(Generic[CT_co])A variable annotated with
Cmay accept a value of typeC. In contrast, a variable annotated withType[C]may accept values that are classes themselves - specifically, it will accept the class object ofC.
The documentation includes an example with the int class:
a = 3 # Has type 'int' b = int # Has type 'Type[int]' c = type(a) # Also has type 'Type[int]'
Update 2024: Type is now deprecated in favour of type
Willem Van Onsem's answer is of course correct, but I'd like to offer a small update. In PEP 585, type hinting generics were introduced in standard collections. For example, whereas we previously had to say e.g.
from typing import Dict
foo: Dict[str, str] = { "bar": "baz" }
we can now forgo the parallel type hierarchy in the typing module and simply say
foo: dict[str, str] = { "bar": "baz" }
This feature is available in python 3.9+, and also in 3.7+ if using from __future__ import annotations.
In terms of this specific question, it means that instead of from typing import Type, we can now simply annotate classes using the built-in type:
def FuncA(arg: type[CustomClass]):
As explained here, you can use Type:
from typing import Type
class X:
"""some class"""
def foo_my_class(my_class: Type[X], bar: str) -> None:
""" Operate on my_class """
I could be wrong, as I haven't been getting down and dirty in Python 3.5 as yet, but looking at the documentation you should be able to do it with typing.Optional. A brief example.
>>> from typing import Optional
>>>
>>> class MyClass(object):
>>> def __init__(self):
>>> self.a = 1
>>>
>>> O = Optional[MyClass]
>>>
>>> def test(x: O) -> int:
>>> return x.a
>>>
>>> myclass = MyClass()
>>> print test(myclass)
1
Hope that helps.
The name Foo doesn't yet exist, so you need to use 'Foo' instead. (mypy and other type checkers should recognize this as a forward reference.)
def __eq__(self, other: 'Foo'):
return self.id == other.id
Alternately, you can use
from __future__ import annotations
which prevents evaluation of all annotations and simply stores them as strings for later reference. (This will be the default in Python 3.10.)
Finally, as also pointed out in the comments, __eq__ should not be hinted this way in the first place. The second argument should be an arbitrary object; you'll return NotImplemented if you don't know how to compare your instance to it. (Who knows, maybe it knows how to compare itself to your instance. If Foo.__eq__(Foo(), Bar()) returns NotImplemented, then Python will try Bar.__eq__(Bar(), Foo()).)
from typing import Any
def __eq__(self, other: Any) -> bool:
if isinstance(other, Foo):
return self.id == other.id
return NotImplemented
or using duck-typing,
def __eq__(self, other: Any) -> bool:
# Compare to anything with an `id` attribute
try:
return self.id == other.id
except AttributeError:
return NotImplemented
In either case, the Any hint is optional.
As of python 3.11 it's now possible to use Self to type hint the current class.
For example, this is now valid python:
from typing import Self
class Foo:
def return_self(self) -> Self:
...
return self
Here is a link to the docs
And is a link to another answer on stack overflow
"self" references in type checking are typically done using strings:
class Node:
def append_child(self, node: 'Node'):
if node != None:
self.first_child = node
self.child_nodes += [node]
This is described in the "Forward references" section of PEP-0484.
Please note that this doesn't do any type-checking or casting. This is a type hint which python (normally) disregards completely1. However, third party tools (e.g. mypy), use type hints to do static analysis on your code and can generate errors before runtime.
Also, starting with python3.7, you can implicitly convert all of your type-hints to strings within a module by using the from __future__ import annotations (and in python4.0, this will be the default).
1The hints are introspectable -- So you could use them to build some kind of runtime checker using decorators or the like if you really wanted to, but python doesn't do this by default.
Postponed evaluation of annotations
PEP 563 introduced postponed evaluations in Python 3.7, stored in __annotations__ as strings. A user can enable this through the __future__ directive:
from __future__ import annotations
This makes it possible to write:
class C:
a: C
def foo(self, b: C):
...
This behaviour was originally planned to become mandatory in Python 4.0, then Python 3.10, but as of Python 3.13, it is still not mandatory. As of October 2024, no decision has been taken on when it will be mandatory.
Heres an illustration of what I want to do:
class Foo(object):
def fn(self) -> Foo: # <-- error here
return Foo()When doing this, the compiler complains:
NameError: name 'Foo' is not defined
How do I use Foo in a type hint while defining Foo itself?
You can just do "Foo", in quotes, and it'll work fine. Python 3.7 has some things that help with that.
The closest thing I’ve come to solving this is:
import typing
Foo = typing.TypeVar('Foo', bound='Foo')
class Foo(object):
def fn(self) -> Foo # Now OK
return Foo()
Is there a cleaner solution?