2016-04-19 5 views
2

У меня проблемы с внедрением простой в использовании абстрактной фабрики.Реализация фабричного шаблона Python с метаклассом

Цель

Чтобы иметь возможность определить конкретные заводы таким образом:

class MyConcreteFactory(...): 
    @classmethod 
    def __load(cls, key): 
     obj = ... # Loading instructions here 
     return obj 

Чтобы иметь возможность использовать concretre заводы этот путь

obj = MyConcreteFactory[key] 

Моя попытка

Я попытался для определения метакласса для заводов, которые переопределяют оператор скобки и инкапсулируют е заводы модель:

class __FactoryMeta(type): 

    __ressources = {} 

    @classmethod 
    def __getitem__(cls, key): 
     if key not in cls.__ressources: 
      cls.__ressources[key] = cls.__load(key) 
     return cls.__ressources[key] 

    @classmethod 
    def __load(cls, key): 
     raise NotImplementedError 


class ConcreteFactory(metaclass=__FactoryMeta): 

    @classmethod 
    def __load(cls, key): 
     return "toto" 


a = ConcreteFactory["mykey"] 
print(a) 

Выпуска

Это не удалось, потому что метод __load называется это один из метакласса, а не один из конкретного класса. Результат:

Traceback (most recent call last): 
    File "C:\Users\walter\workspace\Game\src\core\factories.py", line 34, in <module> 
    a = ConcreteFactory["mykey"] 
    File "C:\Users\walter\workspace\Game\src\core\factories.py", line 19, in __getitem__ 
    cls.__ressources[key] = cls.__load(key) 
    File "C:\Users\walter\workspace\Game\src\core\factories.py", line 24, in __load 
    raise NotImplementedError 
NotImplementedError 

Я попытался удалить метод __load из мета-класса, но затем я получил это (предсказуемую) ошибку:

Traceback (most recent call last): 
    File "C:\Users\walter\workspace\Game\src\core\factories.py", line 30, in <module> 
    a = ConcreteFactory["mykey"] 
    File "C:\Users\walter\workspace\Game\src\core\factories.py", line 19, in __getitem__ 
    cls.__ressources[key] = cls.__load(key) 
AttributeError: type object '__FactoryMeta' has no attribute '_FactoryMeta__load' 

Вопросы

Есть ли способ доступа метод класса из метакласса? Я ошибаюсь и должен делать это по-другому? Тогда каким образом?

Решение

class __FactoryMeta(type): 

    ressources = {} 

    def __getitem__(cls, key): 
     if key not in cls.ressources: 
      cls.ressources[key] = cls.load(key) 
     return cls.ressources[key] 

    def load(cls, key): 
     raise NotImplementedError 


class ConcreteFactory(metaclass=__FactoryMeta): 

    @classmethod 
    def load(cls, key): 
     return "toto" 


a = ConcreteFactory["mykey"] 
print(a) 
+3

По крайней мере, часть вашей проблемы связана с названием mangling, см., Например, http://stackoverflow.com/q/7456807/3001761 – jonrsharpe

+0

Не является метаклассом (почти) только примером, где двойное подчеркивание не является плохой практикой? –

+1

Джон прав. '__name' получает искаженный интерпретатор с явной целью сделать его недоступным для суперкласса. Измените '__load' на' _load', и вы избавитесь от ошибки _that_ (хотя у вас могут быть и некоторые из '__resources' тоже - в зависимости от того, как вы используете' __getitem__', в подклассах). – mgilson

ответ

1

Вы не должны использовать @classmethod в метакласса. Экземпляр метакласса является сам так класс:

def __getitem__(cls, key): 

на самом деле класс и:

@classmethod 
def __getitem__(metacls, key): 

получает метакласса в качестве первого аргумента.

Eiher case, я считаю, что метаклассы усложняют эту проблему. Я считаю, что более жизнеспособным подходом было бы создание базового заводского класса, подкласса его соответствующим образом и использование экземпляра подкласса как фабрики.

+0

Ty для вашей помощи, он решает проблему (+ проблема с механизмом), но я хотел бы переопределить оператор скобок для их использования и определения. Есть ли способ сделать это без метакласса? –