Вызов super в классе, наследующем метакласс, выдает ошибку - PullRequest
0 голосов
/ 29 мая 2018

Я пытаюсь использовать автоматическую регистрацию подклассов с использованием метакласса,
, но приведенный ниже код выдает ошибку в классе 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 голосов
/ 29 мая 2018

Вы пытаетесь создать экземпляр класса до того, как оператор class завершил .Объект класса создан, но еще не присвоен глобальному имени.

Порядок событий:

  • найден оператор class Dep_A и класстело выполняется для формирования атрибутов (создается функция __init__).
  • метод метакласса __new__ вызывается с именем класса, основами и пространством имен тела класса ('Dep_A', (Department,) и{'__init__': <function ...>, ...} соответственно).
  • метакласс создает новый объект класса
  • Метакласс вызывает registry(), передавая новый объект класса
  • registry() вызывает классobject
  • Метод класса __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'>}}
0 голосов
/ 29 мая 2018

Ваша register функция пытается создать экземпляр Dep_A до того, как глобальное имя будет фактически определено.(То есть он вызывается в то время, когда тело оператора класса еще выполняется, прежде чем Dep_A присваивается результат вызова метаклассу.)

Вы хотите сохранить класс сама вместо:

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