Python type hinting own class in method
Question:
Edit: I notice people commenting about how the type hint should not be used with __eq__
, and granted, it shouldn’t. But that’s not the point of my question. My question is why can’t the class be used as type hint in the method parameters, but can be used in the method itself?
Python type hinting has proven very useful for me when working with PyCharm. However, I’ve come across behaviour I find strange, when trying to use a class’ own type in its methods.
For example:
class Foo:
def __init__(self, id):
self.id = id
pass
def __eq__(self, other):
return self.id == other.id
Here, when typing other.
, the property id
is not offered automatically. I was hoping to solve it by defining __eq__
as follows:
def __eq__(self, other: Foo):
return self.id == other.id
However, this gives NameError: name 'Foo' is not defined
. But when I use the type within the method, id
is offered after writing other.
:
def __eq__(self, other):
other: Foo
return self.id == other.id
My question is, why is it not possible to use the class’ own type for type hinting the parameters, while it is possible within the method?
Answers:
since you dont specify the type of the input the ide cant understand what you are dealing with.
try:
class Foo:
def __init__(self, id):
self.id = id
pass
def __eq__(self, other: Foo):
return self.id == other.id
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
Edit: I notice people commenting about how the type hint should not be used with __eq__
, and granted, it shouldn’t. But that’s not the point of my question. My question is why can’t the class be used as type hint in the method parameters, but can be used in the method itself?
Python type hinting has proven very useful for me when working with PyCharm. However, I’ve come across behaviour I find strange, when trying to use a class’ own type in its methods.
For example:
class Foo:
def __init__(self, id):
self.id = id
pass
def __eq__(self, other):
return self.id == other.id
Here, when typing other.
, the property id
is not offered automatically. I was hoping to solve it by defining __eq__
as follows:
def __eq__(self, other: Foo):
return self.id == other.id
However, this gives NameError: name 'Foo' is not defined
. But when I use the type within the method, id
is offered after writing other.
:
def __eq__(self, other):
other: Foo
return self.id == other.id
My question is, why is it not possible to use the class’ own type for type hinting the parameters, while it is possible within the method?
since you dont specify the type of the input the ide cant understand what you are dealing with.
try:
class Foo:
def __init__(self, id):
self.id = id
pass
def __eq__(self, other: Foo):
return self.id == other.id
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