Jawaban:
Untuk mendapatkan salinan objek yang sepenuhnya independen, Anda dapat menggunakan copy.deepcopy()
fungsi ini.
Untuk perincian lebih lanjut tentang penyalinan yang dangkal dan dalam, silakan merujuk ke jawaban lain untuk pertanyaan ini dan penjelasan yang bagus dalam jawaban ini untuk pertanyaan terkait .
Bagaimana saya bisa membuat salinan objek dengan Python?
Jadi, jika saya mengubah nilai bidang objek baru, objek lama seharusnya tidak terpengaruh oleh itu.
Maksudmu objek yang bisa berubah kemudian.
Di Python 3, daftar dapatkan copy
metode (dalam 2, Anda akan menggunakan slice untuk membuat salinan):
>>> 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
Salinan dangkal hanyalah salinan dari wadah terluar.
list.copy
adalah salinan dangkal:
>>> 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'}}]
Anda tidak mendapatkan salinan objek interior. Mereka adalah objek yang sama - jadi ketika mereka bermutasi, perubahan muncul di kedua wadah.
Salinan dalam adalah salinan rekursif dari setiap objek interior.
>>> 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'}}]
Perubahan tidak tercermin dalam aslinya, hanya di salinan.
Benda tidak permanen biasanya tidak perlu disalin. Bahkan, jika Anda mencoba, Python hanya akan memberi Anda objek asli:
>>> 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'
Tuples bahkan tidak memiliki metode penyalinan, jadi mari kita coba dengan irisan:
>>> tuple_copy_attempt = a_tuple[:]
Tapi kita lihat itu objek yang sama:
>>> tuple_copy_attempt is a_tuple
True
Demikian pula untuk string:
>>> s = 'abc'
>>> s0 = s[:]
>>> s == s0
True
>>> s is s0
True
dan untuk frozenset, meskipun mereka memiliki copy
metode:
>>> a_frozenset = frozenset('abc')
>>> frozenset_copy_attempt = a_frozenset.copy()
>>> frozenset_copy_attempt is a_frozenset
True
Objek yang tidak dapat diubah harus disalin jika Anda perlu menyalin objek interior yang dapat diubah.
>>> 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'],)
Seperti yang dapat kita lihat, ketika objek interior dari salinan dimutasi, aslinya tidak berubah.
Objek khusus biasanya menyimpan data dalam suatu __dict__
atribut atau dalam __slots__
(struktur memori mirip tuple).
Untuk membuat objek yang dapat disalin, tentukan __copy__
(untuk salinan dangkal) dan / atau __deepcopy__
(untuk salinan dalam).
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
Catatan yang deepcopy
menyimpan kamus memoisasi id(original)
(atau nomor identitas) untuk salinan. Untuk menikmati perilaku yang baik dengan struktur data rekursif, pastikan Anda belum membuat salinan, dan jika sudah, kembalikan.
Jadi mari kita membuat objek:
>>> c1 = Copyable(1, [2])
Dan copy
buat salinan yang dangkal:
>>> c2 = copy(c1)
>>> c1 is c2
False
>>> c2.b.append(3)
>>> c1.b
[2, 3]
Dan deepcopy
sekarang membuat salinan yang dalam:
>>> c3 = deepcopy(c1)
>>> c3.b.append(4)
>>> c1.b
[2, 3]
Salin dangkal dengan 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]
Salinan dalam dengan 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]
Dokumentasi: https://docs.python.org/3/library/copy.html
Diuji pada Python 3.6.5.
Saya percaya yang berikut ini harus bekerja dengan banyak perilakunya yang baik diklasifikasikan dalam Python:
def copy(obj):
return type(obj)(obj)
(Tentu saja, saya tidak berbicara di sini tentang "salinan dalam," yang merupakan cerita yang berbeda, dan yang mungkin bukan konsep yang sangat jelas - seberapa dalam cukup dalam?)
Menurut pengujian saya dengan Python 3, untuk objek yang tidak dapat diubah, seperti tupel atau string, ia mengembalikan objek yang sama (karena tidak perlu membuat salinan dangkal dari objek yang tidak dapat diubah), tetapi untuk daftar atau kamus ia membuat salinan dangkal independen .
Tentu saja metode ini hanya berfungsi untuk kelas yang konstruktornya berperilaku sesuai. Kasing penggunaan yang memungkinkan: membuat salinan dangkal kelas kontainer Python standar.
__init__
metode mereka . Jadi, saya pikir metode ini mungkin cukup baik untuk tujuan tertentu. Bagaimanapun, saya akan tertarik pada komentar informatif tentang saran ini.
class Foo(object): def __init__(self, arg): super(Foo, self).__init__() self.arg = arg
Dasar saat mendapat. If I do foo = Foo(3) bar = copy(foo) print(foo.arg) # 3 print(bar.arg) # <__main__.Foo object at ...>
Meaning bahwa copy
fungsi Anda rusak bahkan untuk kelas paling dasar sekalipun. Sekali lagi, ini adalah trik yang rapi (karenanya tidak ada DV), tetapi bukan jawaban.
copy.copy
metode untuk membuat salinan yang dangkal, tetapi, mungkin secara naif, menurut saya seharusnya menjadi tanggung jawab kelas untuk menyediakan "konstruktor salinan yang dangkal." Dalam kasus seperti itu, mengapa tidak menyediakan antarmuka yang sama dengannya dict
dan list
melakukan? Jadi, jika kelas Anda ingin bertanggung jawab untuk menyalin objeknya, mengapa tidak menambahkan if isinstance(arg, type(self))
klausa __init__
?