Deepcopy не уважает метакласс - PullRequest
3 голосов
/ 27 марта 2012

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

Я могу обойти эту ошибку, унаследовав от базового класса singleton -типа, но я бы не стал этого делать, по причинам, указанным в в этом вопросе .

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

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

Ответы [ 2 ]

2 голосов
/ 27 марта 2012

Когда вам нужно настроить создание класса (, а не создание экземпляра), вы делаете это в методе __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

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

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

2 голосов
/ 27 марта 2012

Документы в модуле copy говорят следующее:

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

Поэтому, если вы объявите, что они возвращают self, это должно сработать.

...