Я понимаю разницу между copy
vs. deepcopy
в модуле копирования. Я использовал copy.copy
и copy.deepcopy
прежде , чем успешно, но это первый раз , когда я на самом деле пошел о перегрузках __copy__
и __deepcopy__
методы. Я уже Гугл и посмотрел через встроенные в Python модулей , чтобы искать экземпляры __copy__
и __deepcopy__
функции (например sets.py
, decimal.py
, иfractions.py
), но я до сих пор не 100% уверено , что я получил это право.
Вот мой сценарий:
У меня есть объект конфигурации. Первоначально я собираюсь создать экземпляр одного объекта конфигурации с набором значений по умолчанию. Эта конфигурация будет передана нескольким другим объектам (чтобы гарантировать, что все объекты начинаются с одной и той же конфигурации). Однако, как только начинается взаимодействие с пользователем, каждый объект должен настраивать свои конфигурации независимо, не влияя на конфигурации друг друга (что говорит мне, что мне нужно сделать глубокие копии моей начальной конфигурации, чтобы передать их).
Вот образец объекта:
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
дать мне правильное поведение?
источник
Ответы:
Рекомендации по настройке находятся в самом конце страницы документации :
Поскольку вы, похоже, не заботитесь о настройке травления, определение
__copy__
и__deepcopy__
определенно кажется правильным путем для вас.В частности,
__copy__
(мелкая копия) в вашем случае довольно просто ...:__deepcopy__
будет аналогично (memo
тоже принимает аргумент), но перед возвратом он должен будет вызватьself.foo = deepcopy(self.foo, memo)
любой атрибут,self.foo
который требует глубокого копирования (по сути, атрибуты, которые являются контейнерами - списки, dicts, непримитивные объекты, которые содержат другие данные через свои__dict__
s).источник
__copy__
/__deepcopy__
.self.foo = deepcopy(self.foo, memo)
...? Вы действительно имеете в видуnewone.foo = ...
?__init__
. Это не то, что делает копия. Также очень часто бывает, что травление и копирование должны отличаться. На самом деле, я даже не знаю, почему copy пытается использовать протокол травления по умолчанию. Копирование предназначено для манипуляций в памяти, травление - для сохраняемости между эпохами; это совершенно разные вещи, которые мало связаны друг с другом.Собрав вместе ответ Алекса Мартелли и комментарий Роба Янга, вы получите следующий код:
отпечатки
здесь
__deepcopy__
заполняетmemo
dict, чтобы избежать избыточного копирования в случае, если на сам объект ссылается его член.источник
Transporter
?Transporter
- это имя моего класса, который я пишу. Для этого класса я хочу переопределить поведение deepcopy.Transporter
?__deepcopy__
следует включить тест, чтобы избежать бесконечной рекурсии: <! - language: lang-python -> d = id (self) result = memo.get (d, None), если результат не равен None: return resultmemo[id(self)]
самом деле используется, чтобы предотвратить бесконечную рекурсию. Я собрал короткий пример, который предполагает, чтоcopy.deepcopy()
внутренне прерывает вызов объекта, еслиid()
это ключmemo
, правильно? Также стоит отметить , что ,deepcopy()
кажется, делает это самостоятельно , по умолчанию , что делает его трудно представить себе случай , когда Defining__deepcopy__
вручную фактически необходимо ...Следуя отличному ответу Питера , для реализации настраиваемой глубокой копии с минимальными изменениями в реализации по умолчанию (например, просто изменив поле, как мне нужно):
источник
delattr(self, '__deepcopy__')
тогдаsetattr(self, '__deepcopy__', deepcopy_method)
?Из вашей проблемы не ясно, почему вам нужно переопределить эти методы, поскольку вы не хотите вносить какие-либо изменения в методы копирования.
В любом случае, если вы хотите настроить глубокую копию (например, путем совместного использования одних атрибутов и копирования других), вот решение:
источник
__deepcopy__
сброс метода, поскольку у него будет__deepcopy__
= None?__deepcopy__
метод не найден (илиobj.__deepcopy__
возвращает None), тоdeepcopy
возвращается к стандартной функции глубокого копирования. Это можно увидеть здесь__deepcopy__=None
атрибут из клона. Смотрите новый код.Я могу быть немного не в деталях, но вот оно;
Из
copy
документов ;Другими словами:
copy()
скопирует только верхний элемент, а остальные оставит как указатели на исходную структуру.deepcopy()
будет рекурсивно копировать все.То есть то,
deepcopy()
что вам нужно.Если вам нужно сделать что-то действительно конкретное, вы можете переопределить
__copy__()
или__deepcopy__()
, как описано в руководстве. Лично я бы, вероятно, реализовал простую функцию (например,config.copy_config()
или такую), чтобы было ясно, что это не стандартное поведение Python.источник
__copy__(
) и__deepcopy__()
. docs.python.org/library/copy.htmlВ
copy
конечном итоге модуль использует протокол__getstate__()
/ pickling , так что это также допустимые цели для переопределения.__setstate__()
Реализация по умолчанию просто возвращает и устанавливает
__dict__
класс, поэтому вам не нужно вызыватьsuper()
и беспокоиться о хитрости Эйно Гурдена, описанной выше .источник
Основываясь на чистом ответе Энтони Хэтчкинса, вот моя версия, в которой рассматриваемый класс является производным от другого настраиваемого класса (нам нужно вызвать
super
):источник