Torilla
Torilla

Reputation: 403

Metaclass hash, __eq__ and isinstance

I have implemented a class and its metaclass, both defining binary dunder methods including __eq__ and __hash__. The metaclass' __eq__ method should simply instantiate the class and then pass the comparision to the class operator. But when I use isinstance on my classes, I end up in an infinite recursion because isinstance seems to be calling the metaclass' __eq__ method which in turn calls the class' __eq__ method which calls isinstance again.

from abc import ABCMeta

class Metaclass(ABCMeta):
    def __eq__(cls, other):
        return cls() == other

    def __hash__(cls):
        return 0 #would be implemented accordingly with a correct hastype

class Myclass(metaclass=Metaclass):
    def __eq__(self, other):
        if isinstance(other, Myclass):
            """dostuff"""
        else:
            return NotImplemented

class Childclass(Myclass): pass

isinstance(Childclass, Myclass)

This is the result for me in python3.6.7:

  [...]
    File "./test.py", line 14, in __eq__
    if isinstance(other, Myclass):
  File "/usr/lib/python3.6/abc.py", line 193, in __instancecheck__
    return cls.__subclasscheck__(subclass)
  File "/usr/lib/python3.6/abc.py", line 218, in __subclasscheck__
    if cls in getattr(subclass, '__mro__', ()):
  File "./test.py", line 7, in __eq__
    return cls() == other
  File "./test.py", line 14, in __eq__
    if isinstance(other, Myclass):
  File "/usr/lib/python3.6/abc.py", line 193, in __instancecheck__
    return cls.__subclasscheck__(subclass)
  File "/usr/lib/python3.6/abc.py", line 218, in __subclasscheck__
    if cls in getattr(subclass, '__mro__', ()):
  File "./test.py", line 7, in __eq__
    return cls() == other
  File "./test.py", line 14, in __eq__
    if isinstance(other, Myclass):
  File "/usr/lib/python3.6/abc.py", line 193, in __instancecheck__
    return cls.__subclasscheck__(subclass)
  File "/usr/lib/python3.6/abc.py", line 218, in __subclasscheck__
    if cls in getattr(subclass, '__mro__', ()):
  File "./test.py", line 7, in __eq__
    return cls() == other
RecursionError: maximum recursion depth exceeded while calling a Python object

Upvotes: 0

Views: 974

Answers (1)

blueteeth
blueteeth

Reputation: 3555

isinstance(Childclass, Myclass)

Is never going to do what you think because isinstance takes an instance of a class not the class itself.

And lo and behold, if you instantiate Childclass, this call succeeds.

isinstance(Childclass(), Myclass) # => True

I've just run this, and I can't see an error. Running Python 3.7.5.

In [35]: from abc import ABCMeta
    ...:
    ...: class Metaclass(ABCMeta):
    ...:     def __eq__(cls, other):
    ...:         return cls() == other
    ...:
    ...:     def __hash__(cls):
    ...:         return 0 #would be implemented accordingly with a correct hastype
    ...:
    ...: class Myclass(metaclass=Metaclass):
    ...:     def __eq__(self, other):
    ...:         if isinstance(other, Myclass):
    ...:             """dostuff"""
    ...:         else:
    ...:             return NotImplemented
    ...:
    ...: class Childclass(Myclass): pass
    ...:

In [36]: isinstance(Childclass, Myclass)
Out[36]: False

Upvotes: 1

Related Questions