I am monkey patching the __eq__
method of a class. I found that the following works:
def eq(obj, other):
if isinstance(other, str):
return obj.name.upper() == other.upper()
else:
return object.__eq__(obj, other)
This does not work:
def eq(obj, other):
if isinstance(other, str):
return obj.name.upper() == other.upper()
else:
return super().__eq__(other)
This sometimes works, but sometimes raises and error:
def eq(obj, other):
if isinstance(other, str):
return obj.name.upper() == other.upper()
else:
return super().__eq__(self, other)
The error:
<ipython-input-128-91287536205d> in eq(obj, other)
3 return obj.name.upper() == other.upper()
4 else:
----> 5 return super().__eq__(self, other)
6
7
RuntimeError: super(): __class__ cell not found
Can you explain what is going on here? How do I properly replace object
with super()
?
You can't use super()
without arguments in a function defined outside of a class. The __class__
cell super()
relies on is only provided for functions defined in a class
body. From the super()
documentation:
The zero argument form only works inside a class definition, as the compiler fills in the necessary details to correctly retrieve the class being defined, as well as accessing the current instance for ordinary methods.
Use the 2-argument form, naming the class explicitly:
def eq(obj, other):
if isinstance(other, str):
return obj.name.upper() == other.upper()
else:
return super(ClassYouPutThisOn, obj).__eq__(other)
ClassYouPutThisOn.__eq__ = eq
This requires you to explicitly name the class in the monkey patch, making it less useful for reuse.
Instead, you can provide the required __class__
cell manually by nesting eq
in another function with __class__
as a local name:
def patch_eq(cls):
__class__ = cls # provide closure cell for super()
def eq(obj, other):
if isinstance(other, str):
return obj.name.upper() == other.upper()
else:
return super().__eq__(other)
cls.__eq__ = eq
super()
finds the second argument (reference to the instance), by taking the first local name from the calling frame (i.e. the first parameter passed into the function call, usually called self
).
Also see Why is Python 3.x's super() magic?
Demo using the nested-function approach:
>>> class Foo:
... name = 'bar'
... def __eq__(self, other):
... return False
...
>>> Foo() == 'Bar'
False
>>> Foo() == Foo()
False
>>> patch_eq(Foo)
>>> Foo() == 'Bar'
True
>>> Foo() == Foo()
False