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

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

我正在修补 class 的 __eq__ 方法。我发现以下工作:

   def eq(obj, other):
       if isinstance(other, str):
          return obj.name.upper() == other.upper()
       else:
          return object.__eq__(obj, other)

这不起作用:

  def eq(obj, other):
     if isinstance(other, str):
         return obj.name.upper() == other.upper()
     else:
        return super().__eq__(other)

这有时有效,但有时会引发错误:

def eq(obj, other):
   if isinstance(other, str):
       return obj.name.upper() == other.upper()
   else:
       return super().__eq__(self, other)

错误:

<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

你能解释一下这是怎么回事吗?如何将 object 正确替换为 super()

您不能在 class 之外定义的函数中使用不带参数的 super()__class__ 单元 super() 所依赖的仅提供给 class 主体中定义的函数。来自 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.

使用双参数形式,明确命名 class:

def eq(obj, other):
   if isinstance(other, str):
       return obj.name.upper() == other.upper()
   else:
       return super(ClassYouPutThisOn, obj).__eq__(other)

ClassYouPutThisOn.__eq__ = eq

这需要您在 monkey 补丁中明确命名 class,从而降低其重用性。

相反,您可以通过将 eq 嵌套在另一个以 __class__ 作为本地名称的函数中,手动 提供所需的 __class__ 单元格

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() 通过从调用帧中获取第一个本地名称(即传递给函数调用的第一个参数,通常称为 self)找到第二个参数(对实例的引用) .

另见 Why is Python 3.x's super() magic?

使用嵌套函数方法的演示:

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