Как правильно переопределить операции копирования / deepcopy для объекта в Python?
так просто установить, Я чувствую, что я понимаю разницу между copy и deepcopy в модуле копирования и я использовал copy.copy и copy.deepcopy прежде чем успешно, но это первый раз, когда я на самом деле пошел о перегрузке __copy__ и __deepcopy__ методы. Я уже погуглил и просмотрел встроенные модули Python, чтобы найти экземпляры __copy__ и __deepcopy__ функции (например,sets.py,decimal.py и fractions.py), но я все еще не на 100% уверен, что у меня есть это правильно.
вот мой сценарий:
у меня есть объект конфигурации, который в основном состоит из простых свойств (хотя в принципе есть списки других непримитивные объекты в нем). Первоначально я собираюсь создать экземпляр одного объекта конфигурации с набором значений по умолчанию. Эта конфигурация будет передана нескольким другим объектам (чтобы гарантировать, что все объекты начинаются с одной и той же конфигурации). Однако после запуска взаимодействия с пользователем каждый объект будет нужно иметь возможность настраивать конфигурации независимо, не влияя на конфигурации друг друга (что говорит мне, что мне нужно будет сделать deepcopys моей начальной конфигурации для передачи).
вот пример объекта:
class ChartConfig(object):
def __init__(self):
#Drawing properties (Booleans/strings)
self.antialiased = None
self.plot_style = None
self.plot_title = None
self.autoscale = None
#X axis properties (strings/ints)
self.xaxis_title = None
self.xaxis_tick_rotation = None
self.xaxis_tick_align = None
#Y axis properties (strings/ints)
self.yaxis_title = None
self.yaxis_tick_rotation = None
self.yaxis_tick_align = None
#A list of non-primitive objects
self.trace_configs = []
def __copy__(self):
pass
def __deepcopy__(self, memo):
pass
Как правильно выполнять copy и deepcopy методы на этом объекте для обеспечения copy.copy и copy.deepcopy дайте мне правильное поведение? В настоящее время я использую Python 2.6.2.
спасибо заранее!
7 ответов:
рекомендации по настройке находятся в самом конце документы-страницы:
классы могут использовать одни и те же интерфейсы для управление копированием, которое они используют контроль маринования. Смотрите описание рассола модуля для информации дальше данные методы. Модуль копирования не используйте регистрацию copy_reg модуль.
для того, чтобы класс определил свой собственный скопируйте реализацию, она может определить специальные методы
__copy__()и__deepcopy__(). Первый призван реализовать мелкую копию операция; никаких дополнительных аргументов нет пройденный. Последний призван: реализуйте операцию глубокого копирования; это передается один аргумент, памятка словарь. Если__deepcopy__()внедрение необходимо сделать глубоким копия компонента, он должен вызвать элемент
собирая ответ Алекса Мартелли и комментарий Роба Янга, вы получаете следующий код:
from copy import copy, deepcopy class A(object): def __init__(self): print 'init' self.v = 10 self.z = [2,3,4] def __copy__(self): cls = self.__class__ result = cls.__new__(cls) result.__dict__.update(self.__dict__) return result def __deepcopy__(self, memo): cls = self.__class__ result = cls.__new__(cls) memo[id(self)] = result for k, v in self.__dict__.items(): setattr(result, k, deepcopy(v, memo)) return result a = A() a.v = 11 b1, b2 = copy(a), deepcopy(a) a.v = 12 a.z.append(5) print b1.v, b1.z print b2.v, b2.zпечать
init 11 [2, 3, 4, 5] 11 [2, 3, 4]здесь
__deepcopy__заполняетmemodict, чтобы избежать избыточного копирования в случае, если сам объект с его членом.
я мог бы быть немного не в курсе специфики, но здесь идет;
С
copydocs;
- мелкая копия создает новый составной объект, а затем (насколько это возможно) вставляет в него ссылки на объекты, найденные в оригинале.
- глубокая копия создает новый составной объект, а затем рекурсивно вставляет в него копии объектов, найденных в оригинале.
другими словами:
copy()будет копировать только верхний элемент и оставить остальные в качестве указателей в исходную структуру.deepcopy()будет рекурсивно копировать все.то есть
deepcopy()это то, что вам нужно.Если вам нужно сделать что-то действительно особенное, вы можете переопределить
__copy__()или__deepcopy__(), как описано в руководстве. Лично я бы, вероятно, реализовал простую функцию (например,config.copy_config()или такой), чтобы было ясно, что это не стандарт Python поведение.
после отличный ответ Питера, чтобы реализовать пользовательскую deepcopy, с минимальным изменением реализации по умолчанию (например, просто изменение поля, как мне нужно):
class Foo(object): def __deepcopy__(self, memo): deepcopy_method = self.__deepcopy__ self.__deepcopy__ = None cp = deepcopy(self, memo) self.__deepcopy__ = deepcopy_method # custom treatments # for instance: cp.id = None return cp
его не ясно из вашей проблемы, почему вам нужно переопределить эти методы, так как вы не хотите делать какие-либо настройки для методов копирования.
во всяком случае, если вы хотите настроить глубокую копию (например, путем совместного использования некоторых атрибутов и копирования других), вот решение:
from copy import deepcopy def deepcopy_with_sharing(obj, shared_attribute_names, memo=None): ''' Deepcopy an object, except for a given list of attributes, which should be shared between the original object and its copy. obj is some object shared_attribute_names: A list of strings identifying the attributes that should be shared between the original and its copy. memo is the dictionary passed into __deepcopy__. Ignore this argument if not calling from within __deepcopy__. ''' assert isinstance(shared_attribute_names, (list, tuple)) shared_attributes = {k: getattr(obj, k) for k in shared_attribute_names} if hasattr(obj, '__deepcopy__'): # Do hack to prevent infinite recursion in call to deepcopy deepcopy_method = obj.__deepcopy__ obj.__deepcopy__ = None for attr in shared_attribute_names: del obj.__dict__[attr] clone = deepcopy(obj) for attr, val in shared_attributes.iteritems(): setattr(obj, attr, val) setattr(clone, attr, val) if hasattr(obj, '__deepcopy__'): # Undo hack obj.__deepcopy__ = deepcopy_method del clone.__deepcopy__ return clone class A(object): def __init__(self): self.copy_me = [] self.share_me = [] def __deepcopy__(self, memo): return deepcopy_with_sharing(self, shared_attribute_names = ['share_me'], memo=memo) a = A() b = deepcopy(a) assert a.copy_me is not b.copy_me assert a.share_me is b.share_me c = deepcopy(b) assert c.copy_me is not b.copy_me assert c.share_me is b.share_me
основываясь на чистом ответе Энтони Хэтчкинса, вот моя версия, где рассматриваемый класс происходит от другого пользовательского класса (s. t. нам нужно вызвать
super):class Foo(FooBase): def __init__(self, param1, param2): self._base_params = [param1, param2] super(Foo, result).__init__(*self._base_params) def __copy__(self): cls = self.__class__ result = cls.__new__(cls) result.__dict__.update(self.__dict__) super(Foo, result).__init__(*self._base_params) return result def __deepcopy__(self, memo): cls = self.__class__ result = cls.__new__(cls) memo[id(self)] = result for k, v in self.__dict__.items(): setattr(result, k, copy.deepcopy(v, memo)) super(Foo, result).__init__(*self._base_params) return result
The
copyмодуль использует evantually__getstate__()/__setstate__()мариновать протоколом, так что это также допустимые цели для переопределения.реализация по умолчанию просто возвращает и устанавливает
__dict__из класса, так что вам не нужно звонитьsuper()и беспокоиться об умном трюке Эйно Гурдина,выше.
Comments