在metaclass调用的classmethod中调用`super` .__ new__

时间:2012-10-06 06:48:41

标签: 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
        ...
        return new_class

class FooBar(object):
    __metaclass__ = Metaclass

    @classmethod
    def get_fields(cls):
        ...

(此类代码示例位于Tastypie。)

问题是如果我想这样做:

class NewBar(FooBar):
    @classmethod
    def get_fields(cls):
        super(NewBar, cls).get_fields()
        ...

这不起作用,因为在调用NewBar点时尚未创建super(程序流仍在元类中)。那么,有没有解决方法呢?

我知道可能get_fields方法可能成为元类的方法,但这会使继承更难实现(你必须定义新的元类和类本身,对于想要扩展它的开发人员不好类)。

(Python 2.7。)

3 个答案:

答案 0 :(得分:3)

如果在调用NewBarget_fields无效,您仍然可以在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()
    ...

尽管此代码看起来很有趣,但它可以正常工作,并且比问题中提出的替代方案简单得多。虽然大多数使用非常量第一个参数(例如非限定super)调用super(cls, cls)是不正确的并且中断继承,但这个是安全的,因为生成器表达式只是一种非传统的方式来保持NewBar

在MRO中查找clas时,我们会检查类和模块名称(可用__name__as pointed out by Mitar),以避免在othermodule.NewBar继承自thismodule.NewBar时出现误报}}

答案 1 :(得分:0)

基于来自@ user4815162342的回答,我发现了更简单的解决方案:

try:
    super(NewBar, cls).get_fields()
except NameError, e:
    if 'NewBar' in str(e):
        super(cls, cls).get_fields()
    else:
        raise

答案 2 :(得分:0)

我知道这个问题是特定于python 2.7的,但对于那些使用python 3.6的人,你可以简单地调用super()

class NewBar(FooBar):
    @classmethod
    def get_fields(cls):
        super().get_fields()
        ...