фабрика плагинов Python 3.2: создание экземпляров из класса / метакласса - PullRequest
9 голосов
/ 09 мая 2011

Я рифлю на информацию здесь: Метакласс не вызывается в подклассах

Моя проблема в том, что я не могу создать экземпляр объекта, используя этот реестр классов.Если я использую «обычные» методы конструирования, то кажется, что объекты создаются правильно;но когда я пытаюсь использовать объект класса, связанный с реестром, я получаю сообщение об ошибке, что я передаю неверное количество аргументов.(Кажется, что я вызываю метакласс new , а не мой конструктор ... ??)

Мне не ясно, почему он не работает, потому что я думал, что должен иметь возможность создать экземпляриз объекта класса с использованием синтаксиса callable.

Кажется, я получаю метакласс, помещенный в реестр, а не сам класс?Но я не вижу простого способа получить доступ к самому классу в вызове new .

Вот мой пример кода, в котором не удается создать экземпляр переменной 'd':

registry = [] # list of subclasses

class PluginMetaclass(type):
    def __new__(cls, name, bases, attrs):
        print(cls)
        print(name)
        registry.append((name, cls))
        return super(PluginMetaclass, cls).__new__(cls, name, bases, attrs)

class Plugin(metaclass=PluginMetaclass):
    def __init__(self, stuff):
        self.stuff = stuff

# in your plugin modules
class SpamPlugin(Plugin):
    def __init__(self, stuff):
        self.stuff = stuff

class BaconPlugin(Plugin):
    def __init__(self, stuff):
        self.stuff = stuff

c = SpamPlugin(0)
b = BaconPlugin(0)
mycls = registry[1][1]
d = mycls(0)

Спасибо за любую помощь.

1 Ответ

4 голосов
/ 10 мая 2011

Мне кажется, проблема в том, что параметр cls, передаваемый конструктору метакласса, на самом деле является ссылкой на метакласс, а не на создаваемый класс.Поскольку __new__ - это метод класса PluginMetaclass, он ассоциируется с этим классом, как и любой обычный метод класса.Вы, вероятно, хотите зарегистрировать вновь созданный объект класса, который вы получаете от super(PluginMetaclass, cls).__new__(..).

Эта измененная версия работала для меня на 3.2:

class PluginMetaclass(type):
    def __new__(cls, name, bases, attrs):
        print("Called metaclass: %r" % cls)
        print("Creating class with name: %r" % name)
        newclass = super(PluginMetaclass, cls).__new__(cls, name, bases, attrs)
        print("Registering class: %r" % newclass)
        registry.append((name, newclass))
        return newclass

и вызовы print()показать, что происходит за кулисами:

>>> registry = []
>>>
>>> class Plugin(metaclass=PluginMetaclass):
...     def __init__(self, stuff):
...         self.stuff = stuff
...
Called metaclass: <class '__main__.PluginMetaclass'>
Creating class with name: 'Plugin'
Registering class: <class '__main__.Plugin'>
>>> class SpamPlugin(Plugin):
...     def __init__(self, stuff):
...         self.stuff = stuff
...
Called metaclass: <class '__main__.PluginMetaclass'>
Creating class with name: 'SpamPlugin'
Registering class: <class '__main__.SpamPlugin'>
>>> class BaconPlugin(Plugin):
...     def __init__(self, stuff):
...         self.stuff = stuff
...
Called metaclass: <class '__main__.PluginMetaclass'>
Creating class with name: 'BaconPlugin'
Registering class: <class '__main__.BaconPlugin'>
>>> c = SpamPlugin(0)
>>> b = BaconPlugin(0)
>>> mycls = registry[1][1]
>>> d = mycls(0)
>>> d
<__main__.SpamPlugin object at 0x010478D0>
>>> registry
[('Plugin', <class '__main__.Plugin'>), 
('SpamPlugin', <class '__main__.SpamPlugin'>), 
('BaconPlugin', <class '__main__.BaconPlugin'>)]

Редактировать: @ drone115b также решил эту проблему, используя __init__ вместо __new__ в PluginMetaclass.Это, вероятно, лучший способ пойти в большинстве случаев.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...