Python type hinting own class in method

Viewed 3763

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?

2 Answers

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.

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
Related