在继承 metaclass 的 class 中调用 super 会抛出错误

Calling super in a class inheriting a metaclass throws an error

我正在尝试使用元class、
自动注册子classes 但是下面的代码在 class Dep_A

中抛出错误

super(Dep_A,self).__init__().

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

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

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__()
        ...

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

您想存储class本身

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

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

事件顺序是:

  • 找到class Dep_A语句,执行class体形成属性(创建__init__函数)
  • metaclass __new__ 方法使用 classname、bases 和 class body 命名空间('Dep_A'(Department,){'__init__': <function ...>, ...})。
  • 元class 创建新的class 对象
  • 元class调用registry(),传入新的class对象
  • registry() 调用 class 对象
  • 调用了class__init__方法

通常,Dep_A 是在metaclass __new__ 方法returns 新创建的对象时赋值的。 在那之前 没有分配全局名称 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

或者,不要尝试在注册表中创建实例,而是存储 class。您始终可以在 以后 .

创建单个实例

下面实现了一个注册表,当你需要一个实例时,它会透明地创建一个实例,并只存储创建的一个实例:

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

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

>>> 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'>}}