簡體   English   中英

在繼承元類的類中調用super會引發錯誤

[英]Calling super in a class inheriting a metaclass throws an error

我正在嘗試使用元類使用子類的自動注冊,
但是下面的代碼在該行的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__()
        ...

您正在嘗試class語句完成之前創建該類的實例。 類對象已創建,但尚未分配給全局名稱。

事件的順序是:

  • 找到class Dep_A語句,並執行類主體以形成屬性(創建__init__函數)。
  • 將使用類名,基數和類主體名稱空間(分別為'Dep_A'(Department,){'__init__': <function ...>, ...} )調用元類__new__方法。
  • 元類創建新的類對象
  • 元類調用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'>}}

您的register函數嘗試在實際定義全局名稱之前實例化Dep_A (即,在將Dep_A的調用結果分配給元類之前,在仍然執行類語句的主體時調用它。)

您想存儲類本身

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM