在继承元类的类中调用super会引发错误

时间:2018-05-29 16:21:59

标签: python python-2.x super metaclass

我正在尝试使用元类自动注册子类,
但是以下代码在行

上的类Dep_A 中引发错误

super(Dep_A,self).__init__().

*NameError: global name 'Dep_A' is not defined*

尝试了MetaClass中的注释行,但是出现了同样的错误。 我知道这在不使用元类时有效,所以如何初始化父类的init方法

registry = {}

def register(cls):
    print cls.__name__
    registry[cls.__name__] = cls()
    return cls


class MetaClass(type):
    def __new__(mcs, clsname, bases, attrs):
        # newclass = super(MetaClass, mcs).__new__(mcs, clsname, bases, attrs)
        newclass = type.__new__(mcs,clsname, bases, attrs)
        register(newclass)
        return newclass


class Department(object):
    __metaclass__ = MetaClass

    def __init__(self):
        self.tasks = dict()


class Dep_A(Department):
    def __init__(self):
        super(Dep_A,self).__init__()
        ...

2 个答案:

答案 0 :(得分:2)

您正在尝试在class语句完成之前创建类的实例。已创建类对象,但尚未将其分配给全局名称。

事件的顺序是:

  • 找到class Dep_A语句,并执行类主体以形成属性(创建__init__函数)。
  • 使用classname,bases和类体名称空间(分别为__new__'Dep_A'(Department,))调用元类{'__init__': <function ...>, ...}方法。
  • 元类创建新的类对象
  • 元类调用registry(),传入新的类对象
  • registry()调用类对象
  • __init__方法称为

通常,当元类Dep_A方法返回新创建的对象时,会分配__new__在此之前没有分配全局名称Dep_A,因此super(Dep_A, self).__init__()调用失败。

您可以先从那里设置名称:

class MetaClass(type):
    def __new__(mcs, clsname, bases, attrs):
        # newclass = super(MetaClass, mcs).__new__(mcs, clsname, bases, attrs)
        newclass = type.__new__(mcs,clsname, bases, attrs)
        globals()[clsname] = newclass
        register(newclass)
        return newclass

或者,不要尝试在注册表中创建实例,而是存储一个类。您随后可以在上创建单个实例

以下实现了一个注册表,该注册表在您需要的时候透明地创建实例,并且只存储创建的那个实例:

from collections import Mapping

class Registry(Mapping):
    def __init__(self):
        self._classes = {}
        self._instances = {}

    def __getitem__(self, name):
        if name not in self._instances:
            self._instances[name] = self._classes.pop(name)()
        return self._instances[name]

    def __iter__(self):
        return iter(self._classes.viewkeys() | self._instances.viewkeys())

    def __len__(self):
        return len(self._classes) + len(self._instances)

    def register(self, cls):
        self._classes[cls.__name__] = cls

registry = Registry()

class MetaClass(type):
    def __new__(mcs, clsname, bases, attrs):
        # newclass = super(MetaClass, mcs).__new__(mcs, clsname, bases, attrs)
        newclass = type.__new__(mcs,clsname, bases, attrs)
        registry.register(newclass)
        return newclass

现在类存储在一个单独的映射中,只有当您稍后从注册表映射中查找该类时,才会创建并缓存实例:

>>> class Department(object):
...     __metaclass__ = MetaClass
...     def __init__(self):
...         self.tasks = dict()
...
>>> class Dep_A(Department):
...     def __init__(self):
...         super(Dep_A,self).__init__()
...
>>> registry.keys()
['Department', 'Dep_A']
>>> registry['Dep_A']
<__main__.Dep_A object at 0x110536ad0>
>>> vars(registry)
{'_instances': {'Dep_A': <__main__.Dep_A object at 0x110536ad0>}, '_classes': {'Department': <class '__main__.Department'>}}

答案 1 :(得分:1)

您的register函数在实际定义全局名称之前尝试实例化Dep_A。 (也就是说,在为Dep_A分配调用元类的结果之前,仍在执行类语句的主体时调用它。)

您希望存储类本身

def register(cls):
    registry[cls.__name__] = cls  # Not cls()
    return cls