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()
?
The super() function is used to give access to methods and properties of a parent or sibling class. The super() function returns an object that represents the parent class.
An Overview of Python's super() Function super() alone returns a temporary object of the superclass that then allows you to call that superclass's methods.
Understanding Python super() with __init__() methods When this method is called it allows the class to initialize the attributes of the class. In an inherited subclass, a parent class can be referred with the use of the super() function.
In general it is necessary. And it's often necessary for it to be the first call in your init. It first calls the init function of the parent class ( dict ).
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With