在动态添加的方法中调用 super 的正确方法是什么?

Woo*_*ang 4 python oop metaclass super

我定义了一个元类,它将名为“test”的方法添加到创建的类中:

class FooMeta(type):
    
    def __new__(mcls, name, bases, attrs):
        def test(self):
            return super().test()
        attrs["test"] = test
        cls = type.__new__(mcls, name, bases, attrs)
        return cls
Run Code Online (Sandbox Code Playgroud)

然后我使用这个元类创建两个类

class A(metaclass=FooMeta):
    
    pass
      

class B(A):
    
    pass
Run Code Online (Sandbox Code Playgroud)

当我跑步时

a = A()
a.test()
Run Code Online (Sandbox Code Playgroud)

引发 TypeError 的位置super().test()

super(type, obj): obj must be an instance or subtype of type
Run Code Online (Sandbox Code Playgroud)

这意味着super()无法正确推断父类。如果我将super呼叫更改为

def __new__(mcls, name, bases, attrs):
    def test(self):
        return super(cls, self).test()
    attrs["test"] = test
    cls = type.__new__(mcls, name, bases, attrs)
    return cls
Run Code Online (Sandbox Code Playgroud)

那么引发的错误就变成:

AttributeError: 'super' object has no attribute 'test'
Run Code Online (Sandbox Code Playgroud)

这是预期的,因为 的父级A没有实现test方法。


super()所以我的问题是调用动态添加的方法的正确方法是什么?super(cls, self)在这种情况下我应该总是写吗?如果是这样,那就太丑了(对于python3)!

jsb*_*eno 8

无参数在 Python 中super()非常特殊,因为它在代码编译时本身会触发一些行为:Python 创建一个不可见__class__变量,该变量是对嵌入调用的“物理”class语句体的引用(如果直接使用该变量super(),也会发生这种情况)__class__在类方法内)。

在这种情况下,调用的“物理”类super()是元类FooMeta本身,而不是它正在创建的类。

解决方法是使用super带有 2 个位置参数的版本:将在其中搜索直接超类的类和实例本身。

在Python 2和其他场合,人们可能更喜欢参数化使用super,通常使用类名本身作为第一个参数:在运行时,该名称将作为当前模块中的全局变量使用。也就是说,如果class A要在源文件中静态编码,并使用一个方法,您将在其主体内部def test(...):使用。super(A, self).test(...)

然而,尽管类名不能作为定义元类的模块中的变量使用,但您确实需要将对类的引用作为第一个参数传递给super. 由于 (test) 方法接收self实例的引用,因此其类由 或self.__class__给出type(self)

TL;DR:只需将动态方法中的 super 调用更改为:

class FooMeta(type):
    
    def __new__(mcls, name, bases, attrs):
        def test(self):
            return super(type(self), self).test()
        attrs["test"] = test
        cls = type.__new__(mcls, name, bases, attrs)
        return cls
Run Code Online (Sandbox Code Playgroud)