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

问题描述 投票:0回答:1

我定义了一个元类,它将一个名为“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

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

class A(metaclass=FooMeta):
    
    pass
      

class B(A):
    
    pass

当我跑步时

a = A()
a.test()

super().test()
:

处引发 TypeError
super(type, obj): obj must be an instance or subtype of type

这意味着

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

那么引发的错误就变成:

AttributeError: 'super' object has no attribute 'test'

预计

A
的父级不会实现
test
方法。


所以我的问题是在动态添加的方法中调用

super()
的正确方法是什么?在这种情况下我应该总是写
super(cls, self)
吗?如果是这样,那就太丑了(对于python3)!

python oop super metaclass
1个回答
8
投票

无参数

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

在这种情况下,调用

super()

 的“物理”类是元类 
FooMeta
 本身,而不是它正在创建的类。

解决方法是使用

super

 的版本,它接受 2 个位置参数:将在其中搜索直接超类的类和实例本身。

在Python 2和其他场合,人们可能更喜欢参数化使用

super

,使用类名本身作为第一个参数是正常的:在运行时,该名称将作为当前模块中的全局变量使用。也就是说,如果 
class A
 将使用 
def test(...):
 方法静态编码在源文件中,则您将在其主体内使用 
super(A, self).test(...)

但是,尽管类名在定义元类的模块中不能作为变量使用,但您确实需要将对类的引用作为第一个参数传递给

super

。由于(测试)方法接收 
self
 作为对实例的引用,因此其类由 
self.__class__
type(self)
 给出。

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

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
    
© www.soinside.com 2019 - 2024. All rights reserved.