Python 在从元类调用的classmethod中调用'super'。\u新建__
我有一个例子,我的类有一个自定义元类,它在创建时调用该类的类方法,类似于:Python 在从元类调用的classmethod中调用'super'。\u新建__,python,inheritance,super,Python,Inheritance,Super,我有一个例子,我的类有一个自定义元类,它在创建时调用该类的类方法,类似于: class Metaclass(type): def __new__(cls, name, bases, attrs): ... new_class = super(Metaclass, cls).__new__(cls, name, bases, attrs) ... new_class.get_fields() # do something
class Metaclass(type):
def __new__(cls, name, bases, attrs):
...
new_class = super(Metaclass, cls).__new__(cls, name, bases, attrs)
...
new_class.get_fields() # do something
...
return new_class
class FooBar(object):
__metaclass__ = Metaclass
@classmethod
def get_fields(cls):
...
(此类代码的示例如中所示。)
问题是如果我想做:
class NewBar(FooBar):
@classmethod
def get_fields(cls):
super(NewBar, cls).get_fields()
...
这不起作用,因为在调用super
时,NewBar
尚未创建(程序流仍在元类中)。那么,有什么解决办法吗
我知道可能get_fields
method可以成为元类的一种方法,但这会使继承更难实现(您必须同时定义新的元类和类本身,这对希望扩展此类的开发人员来说并不好)
(Python 2.7)如果调用
get\u字段时NewBar
不可用,您仍然可以在cls
的MRO中找到它:
@classmethod
def get_fields(cls):
# we can get invoked before NewBar is available in globals,
# so get NewBar from cls.__mro__
NewBar = next(c for c in cls.__mro__
if c.__module__ == __name__ and c.__name__ == 'NewBar')
super(NewBar, cls).get_fields()
...
尽管这段代码看起来很有趣,但它工作正常,并且比问题中提出的备选方案简单得多。虽然大多数使用非常量第一个参数(如unqualifiedsuper(cls,cls)
)调用super
)是不正确的,并且会破坏继承,但这一调用是安全的,因为生成器表达式只是获取NewBar
的非常规方法
在MRO中查找CLA时,我们检查类和模块名称(可从@user4815162342获得),以避免在其他模块时出现误报。NewBar
继承自此模块。基于@user4815162342的回答,我发现了更简单的解决方案:
try:
super(NewBar, cls).get_fields()
except NameError, e:
if 'NewBar' in str(e):
super(cls, cls).get_fields()
else:
raise
我知道这个问题是针对Python2.7的,但是,对于那些使用Python3.6的用户,您可以简单地调用super()
class-NewBar(FooBar):
@类方法
def get_字段(cls):
super().get_字段()
...
不幸的是,它以牺牲正确性为代价而变得更简单。特别是,str(e)
中的'NewBar'感觉像是一个等待发生的bug,特别是在get\u字段的每个重写实现中都需要这种代码。为了将来维护该代码,我会认真推荐一种更干净的方法。嗯,是的,但我不能更改原始的get\u字段
方法。是从外部图书馆来的。我明白了。这在您的示例中并不清楚,因为第一个定义get\u字段的FooBar
,已经使用了您的元类,因此可以合理地假设FooBar
,以及整个get\u字段
接口都是由您定义的。那么,元类是否在您的控制之下?如果是这样的话,我想我可以修改我的答案来工作,而不改变get\u字段的签名。也许我应该提供一个直接链接到code-in。我最终使用了:me=next(c代表cls中的c。如果c.\uu mro.\uu模块\uuuuu==\uu名称\uuu和c.\uu名称\uuuu='NewBar')
。我喜欢c模块的改进!我会更新答案。试试看:super\u class=NewBar;除了名称错误:super_class=cls;super(super_类,cls).get_fields()
?@kroolik它可以工作,但它以不同的方式被混淆。答案中的代码清楚地表明,我们只是试图找到NewBar
,以便以最常规的方式调用super
。在您的变体中,super
调用看起来比必要的更神奇;除了名称错误:NewBar=cls
将保留NameError
技巧,但要明确指出,我们确实在寻找NewBar
。如果它不能确保它真的找到了NewBar
,我还是会感到不安。也许这里应该有一个'assert cls.\uuuu name.\uuuuu=='NewBar'。