Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python3 - using super() in __eq__ methods raises RuntimeError: super(): __class__ cell not found

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()?

like image 441
oz123 Avatar asked May 04 '17 09:05

oz123


People also ask

What does super() do in Python?

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.

What does super() return in Python?

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.

What does super () __ Init__ do in Python?

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.

Is super () necessary Python?

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 ).


1 Answers

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
like image 163
Martijn Pieters Avatar answered Sep 20 '22 23:09

Martijn Pieters