Ответы:
Чтобы получить полностью независимую копию объекта, вы можете использовать copy.deepcopy()
функцию.
Для получения более подробной информации о мелком и глубоком копировании, пожалуйста, обратитесь к другим ответам на этот вопрос и хорошему объяснению в этом ответе на связанный вопрос .
Как я могу создать копию объекта в Python?
Итак, если я изменю значения полей нового объекта, на старый объект это не должно повлиять.
Вы имеете в виду изменчивый объект тогда.
В Python 3 списки получают copy
метод (в 2 вы бы использовали фрагмент для создания копии):
>>> a_list = list('abc')
>>> a_copy_of_a_list = a_list.copy()
>>> a_copy_of_a_list is a_list
False
>>> a_copy_of_a_list == a_list
True
Мелкие копии - это всего лишь копии самого внешнего контейнера.
list.copy
это мелкая копия:
>>> list_of_dict_of_set = [{'foo': set('abc')}]
>>> lodos_copy = list_of_dict_of_set.copy()
>>> lodos_copy[0]['foo'].pop()
'c'
>>> lodos_copy
[{'foo': {'b', 'a'}}]
>>> list_of_dict_of_set
[{'foo': {'b', 'a'}}]
Вы не получаете копию предметов интерьера. Это один и тот же объект - поэтому, когда они видоизменены, изменение обнаруживается в обоих контейнерах.
Глубокие копии - это рекурсивные копии каждого предмета интерьера.
>>> lodos_deep_copy = copy.deepcopy(list_of_dict_of_set)
>>> lodos_deep_copy[0]['foo'].add('c')
>>> lodos_deep_copy
[{'foo': {'c', 'b', 'a'}}]
>>> list_of_dict_of_set
[{'foo': {'b', 'a'}}]
Изменения не отражаются в оригинале, только в копии.
Неизменяемые объекты обычно не нужно копировать. Фактически, если вы попытаетесь, Python просто выдаст вам оригинальный объект:
>>> a_tuple = tuple('abc')
>>> tuple_copy_attempt = a_tuple.copy()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'copy'
У кортежей даже нет метода копирования, поэтому давайте попробуем его с фрагментом:
>>> tuple_copy_attempt = a_tuple[:]
Но мы видим, что это тот же объект:
>>> tuple_copy_attempt is a_tuple
True
Аналогично для строк:
>>> s = 'abc'
>>> s0 = s[:]
>>> s == s0
True
>>> s is s0
True
и для фрозенце, хотя у них есть copy
метод:
>>> a_frozenset = frozenset('abc')
>>> frozenset_copy_attempt = a_frozenset.copy()
>>> frozenset_copy_attempt is a_frozenset
True
Неизменяемые объекты должны быть скопированы, если вам нужно скопировать изменяемый внутренний объект.
>>> tuple_of_list = [],
>>> copy_of_tuple_of_list = tuple_of_list[:]
>>> copy_of_tuple_of_list[0].append('a')
>>> copy_of_tuple_of_list
(['a'],)
>>> tuple_of_list
(['a'],)
>>> deepcopy_of_tuple_of_list = copy.deepcopy(tuple_of_list)
>>> deepcopy_of_tuple_of_list[0].append('b')
>>> deepcopy_of_tuple_of_list
(['a', 'b'],)
>>> tuple_of_list
(['a'],)
Как мы видим, когда внутренний объект копии видоизменен, оригинал не изменяется.
Пользовательские объекты обычно хранят данные в __dict__
атрибуте или в __slots__
структуре памяти, подобной кортежу.
Чтобы создать копируемый объект, определите __copy__
(для мелких копий) и / или __deepcopy__
(для глубоких копий).
from copy import copy, deepcopy
class Copyable:
__slots__ = 'a', '__dict__'
def __init__(self, a, b):
self.a, self.b = a, b
def __copy__(self):
return type(self)(self.a, self.b)
def __deepcopy__(self, memo): # memo is a dict of id's to copies
id_self = id(self) # memoization avoids unnecesary recursion
_copy = memo.get(id_self)
if _copy is None:
_copy = type(self)(
deepcopy(self.a, memo),
deepcopy(self.b, memo))
memo[id_self] = _copy
return _copy
Обратите внимание, что deepcopy
хранит словарь памятки id(original)
(или идентификационные номера) для копий. Чтобы получить хорошее поведение с рекурсивными структурами данных, убедитесь, что вы еще не сделали копию, и, если она у вас есть, верните ее.
Итак, давайте сделаем объект:
>>> c1 = Copyable(1, [2])
И copy
делает мелкую копию:
>>> c2 = copy(c1)
>>> c1 is c2
False
>>> c2.b.append(3)
>>> c1.b
[2, 3]
А deepcopy
теперь делает глубокую копию:
>>> c3 = deepcopy(c1)
>>> c3.b.append(4)
>>> c1.b
[2, 3]
Мелкая копия с copy.copy()
#!/usr/bin/env python3
import copy
class C():
def __init__(self):
self.x = [1]
self.y = [2]
# It copies.
c = C()
d = copy.copy(c)
d.x = [3]
assert c.x == [1]
assert d.x == [3]
# It's shallow.
c = C()
d = copy.copy(c)
d.x[0] = 3
assert c.x == [3]
assert d.x == [3]
Глубокая копия с copy.deepcopy()
#!/usr/bin/env python3
import copy
class C():
def __init__(self):
self.x = [1]
self.y = [2]
c = C()
d = copy.deepcopy(c)
d.x[0] = 3
assert c.x == [1]
assert d.x == [3]
Документация: https://docs.python.org/3/library/copy.html
Проверено на Python 3.6.5.
Я считаю, что следующее должно работать со многими классами в Python:
def copy(obj):
return type(obj)(obj)
(Конечно, я не говорю здесь о «глубоких копиях», это отдельная история, и которая может быть не очень ясным понятием - насколько глубоко это глубоко?)
Согласно моим тестам на Python 3, для неизменяемых объектов, таких как кортежи или строки, он возвращает один и тот же объект (поскольку нет необходимости делать поверхностную копию неизменяемого объекта), но для списков или словарей он создает независимую поверхностную копию. ,
Конечно, этот метод работает только для классов, конструкторы которых ведут себя соответственно. Возможные варианты использования: создание мелкой копии стандартного контейнерного класса Python.
__init__
методах. Итак, я подумал, что этот метод может быть достаточно хорош для определенных целей. В любом случае меня заинтересуют информативные комментарии по этому предложению.
class Foo(object): def __init__(self, arg): super(Foo, self).__init__() self.arg = arg
Basic, как он есть. Если я делаю Это foo = Foo(3) bar = copy(foo) print(foo.arg) # 3 print(bar.arg) # <__main__.Foo object at ...>
означает, что ваша copy
функция нарушена даже для самых базовых классов. Опять же, это аккуратный трюк (следовательно, нет DV), но не ответ.
copy.copy
метод для создания мелких копий, но, возможно, наивно, мне кажется, что ответственность за обеспечение «конструктора мелкой копии» должна быть на классе. В таком случае, почему бы не предоставить такой же интерфейс к нему, как dict
и list
делать? Итак, если ваш класс хочет взять на себя ответственность за копирование своих объектов, почему бы не добавить в него if isinstance(arg, type(self))
предложение __init__
?