Відповіді:
Для отримання повністю незалежної копії об'єкта можна скористатися 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
базові, як це виходить. Якщо я маю на 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__
?