在动态添加的方法中调用 super 的正确方法是啥?
Posted
技术标签:
【中文标题】在动态添加的方法中调用 super 的正确方法是啥?【英文标题】:What is the corretly way to call super in dynamically added methods?在动态添加的方法中调用 super 的正确方法是什么? 【发布时间】:2021-10-22 23:13:16 【问题描述】:我定义了一个元类,它将一个名为“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)!
【问题讨论】:
【参考方案1】:无参数 super()
在 Python 中是 非常 特殊的,因为它会在代码编译期间触发一些行为: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:只需将动态方法中的超级调用更改为:
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
【讨论】:
以上是关于在动态添加的方法中调用 super 的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章