2012-03-27 3 views
1

У меня есть класс, который по дизайну должен соответствовать шаблону singleton. Поэтому я пошел дальше и реализовал его с использованием метакласса . Все работало хорошо, пока не сообщалось об ошибке, которая, в общем, говорила, что deepcopy -описанные экземпляры моего singleton не были теми же экземплярами.deepcopy не уважает метакласс

я могу обойти эту ошибку путем наследования от базового одноплодной типа класса, но я скорее не хотел, по причинам, указанный в this question.

рабочий пример этой проблемы представлен ниже:

class SingletonMeta(type): 
    def __init__(cls, name, bases, dict): 
     super(SingletonMeta, cls).__init__(name, bases, dict) 
     cls.instance = None 
    def __call__(cls,*args,**kw): 
     print "SingletonMeta __call__ was called" 
     if cls.instance is None: 
      cls.instance = super(SingletonMeta, cls).__call__(*args, **kw) 
     return cls.instance 

class MyClass1(object): 
    __metaclass__ = SingletonMeta 

class SingletonBase(object): 
    _instance = None 
    def __new__(class_, *args, **kwargs): 
     print "SingletonBase __new__ was called" 
     if not isinstance(class_._instance, class_): 
      class_._instance = object.__new__(class_, *args, **kwargs) 
     return class_._instance 

class MyClass2(SingletonBase): 
    pass 

from copy import deepcopy as dcp 

mm1 = MyClass1() 
mm2 = dcp(mm1) 
print "mm1 is mm2:", mm1 is mm2 

mb1 = MyClass2() 
mb2 = dcp(mb1) 
print "mb1 is mb2:", mb1 is mb2 

Выход:

SingletonMeta __call__ was called 
mm1 is mm2: False 
SingletonBase __new__ was called 
SingletonBase __new__ was called 
mb1 is mb2: True 

Можете ли вы дать мне какие-либо указатели о том, как нужно решить эту проблему? Я бегу на Python 2.7.x

ответ

1

Документах на copy модуле сказать:

Для того класса, чтобы определить свою собственную реализацию копирования, он может определить специальные методы __copy__() и __deepcopy__(). [...] Последний призван для выполнения операции глубокой копии; ему передается один аргумент, словарь заметок. [...]

Так что, если вы объявите их вернуть self, что должен сделать трюк.

1

Когда вам нужно настроить создание класса (не создания экземпляра), вы делаете это в __new__ методе метакласса:

def __new__(cls, name, bases, dict): 
    dict['__deepcopy__'] = dict['__copy__'] = lambda self, *args: self 
    return super(SingletonMeta, cls).__new__(cls, name, bases, dict) 

и ваш тест даст

 
SingletonMeta __call__ was called 
mm1 is mm2: True 

You необходимо определить __copy__, или даже мелкие копии приведут к появлению новых экземпляров.

Рад, что мое решение в этой теме пригодилось.

+0

Единственная проблема с этим решением (а также с @AKX, так как эти два эквивалентны) заключается в том, что если пользователю метакласса «SingletonMeta» нужна пользовательская реализация '__deepcopy__', она будет перезаписана (если я правильно понимаю). Разве это не так? –

+0

@ dare2be Имеет ли смысл иметь пользовательский '__deepcopy__' на одноэлементном? Синглтон означает, что копия никогда не должна быть. Поэтому '__deepcopy__' всегда должна быть функцией идентификации. – agf

+0

Думаю, ты прав. И если кому-то нужно настраивать '__deepcopy__', они не должны использовать синглтон в первую очередь. Поскольку ваши ответы и ответы AKX почти одинаковы, я выберу AKX для того, чтобы быть «первым». И для RTFM. : P Спасибо вам все равно! –