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()
    ...
尽管这段代码看起来很有趣,但它工作正常,并且比问题中提出的备选方案简单得多。虽然大多数使用非常量第一个参数(如unqualified
super(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'。