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

2024-09-23 00:26:19 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试使用元类来自动注册子类,
但是下面的代码在类Dep\u A的行中抛出一个错误

super(Dep_A,self).__init__().

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

尝试元类中的注释行,但出现相同的错误。 我知道这在不使用元类时是有效的,所以如何初始化父类的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__()
        ...

Tags: nameselfnewinitdefattrsclassmetaclass
2条回答

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

您希望存储类本身:

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

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

事件的顺序是:

  • 找到class Dep_A语句,执行类主体以形成属性(创建__init__函数)。你知道吗
  • 调用元类__new__方法时使用类名、基和类主体名称空间('Dep_A'(Department,){'__init__': <function ...>, ...})。你知道吗
  • 元类创建新的类对象
  • 元类调用registry(),传递新的类对象
  • registry()调用类对象
  • 调用类__init__方法

通常,当元类__new__方法返回新创建的对象时,Dep_A被赋值。在此之前,没有分配全局名称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'>}}

相关问题 更多 >