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?

Asked By: R. dV

||

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
Answered By: Dr. Prof. Patrick

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.

Answered By: chepner

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

Answered By: user1239299
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.