Modul `copy` Python
Artikel ini menjelaskan modul copy Python.
Berfokus pada perbedaan antara salinan dangkal dan salinan mendalam, kami memberikan penjelasan yang jelas—mulai dari mekanisme dasar duplikasi objek hingga penerapannya pada kelas kustom—disertai contoh praktis.
YouTube Video
Modul copy Python
Modul copy milik Python adalah modul standar untuk menangani duplikasi (penyalinan) objek. Tujuannya adalah memahami perbedaan antara salinan dangkal dan salinan mendalam serta dapat mengendalikan perilaku penyalinan untuk objek kustom.
Dasar: Apa itu salinan dangkal?
Di sini kami menunjukkan perilaku salinan dangkal untuk objek yang dapat diubah (mutable) seperti list dan dictionary. Salinan dangkal hanya menduplikasi objek tingkat atas dan membagikan referensi di dalamnya.
1# Example: shallow copy with lists and dicts
2import copy
3
4# A nested list
5original = [1, [2, 3], 4]
6shallow = copy.copy(original)
7
8# Mutate nested element
9shallow[1].append(99)
10
11print("original:", original) # nested change visible in original
12print("shallow: ", shallow)Dalam kode ini, daftar tingkat teratas diduplikasi, tetapi subdaftar di dalamnya dibagikan melalui referensi, sehingga shallow[1].append(99) juga tercermin pada original. Ini adalah perilaku khas dari salinan dangkal.
Apa itu salinan mendalam?
Salinan mendalam menduplikasi objek dan semua referensi internalnya secara rekursif, menghasilkan objek yang independen. Gunakan ini ketika Anda ingin menduplikasi struktur bertingkat yang kompleks dengan aman.
1# Example: deep copy with nested structures
2import copy
3
4original = [1, [2, 3], {'a': [4, 5]}]
5deep = copy.deepcopy(original)
6
7# Mutate nested element of the deep copy
8deep[1].append(100)
9deep[2]['a'].append(200)
10
11print("original:", original) # original remains unchanged
12print("deep: ", deep)Pada contoh ini, perubahan pada deep tidak memengaruhi original. deepcopy menyalin seluruh graf objek, menghasilkan replika yang independen.
Cara memutuskan mana yang akan digunakan
Pilihan salinan yang digunakan harus ditentukan oleh struktur objek dan tujuan Anda. Jika Anda tidak ingin perubahan pada elemen internal yang dapat diubah memengaruhi objek asli, deepcopy adalah pilihan yang tepat. Ini karena ia menduplikasi seluruh objek secara rekursif, menciptakan salinan yang sepenuhnya independen.
Sebaliknya, jika menduplikasi hanya objek tingkat atas sudah cukup dan Anda mengutamakan kecepatan serta efisiensi memori, copy (salinan dangkal) lebih sesuai.
- Anda ingin menghindari perubahan pada objek asli saat Anda memodifikasi elemen internal yang dapat diubah → gunakan
deepcopy. - Menduplikasi hanya tingkat atas sudah cukup dan Anda ingin memprioritaskan performa (kecepatan/memori) → gunakan
copy(salinan dangkal).
Menangani objek bawaan dan objek immutabel
Objek immutabel (misalnya int, str, dan tuple yang isinya immutabel) biasanya tidak perlu disalin. copy.copy dapat mengembalikan objek yang sama bila sesuai.
1# Example: copying immutables
2import copy
3
4a = 42
5b = copy.copy(a)
6print(a is b) # True for small ints (implementation detail)
7
8s = "hello"
9t = copy.deepcopy(s)
10print(s is t) # True (strings are immutable)Karena menyalin objek immutabel memberikan sedikit manfaat efisiensi, objek yang sama dapat digunakan kembali. Hal ini jarang menjadi sesuatu yang perlu Anda khawatirkan dalam desain aplikasi.
Kelas kustom: mendefinisikan __copy__ dan __deepcopy__
Jika penyalinan default tidak sesuai dengan yang Anda harapkan untuk kelas Anda, Anda dapat menyediakan logika penyalinan sendiri. Jika Anda mendefinisikan __copy__ dan __deepcopy__, maka copy.copy dan copy.deepcopy akan menggunakannya.
1# Example: customizing copy behavior
2import copy
3
4class Node:
5 def __init__(self, value, children=None):
6 self.value = value
7 self.children = children or []
8
9 def __repr__(self):
10 return f"Node({self.value!r}, children={self.children!r})"
11
12 def __copy__(self):
13 # Shallow copy: create new Node, but reuse children list reference
14 new = self.__class__(self.value, self.children)
15 return new
16
17 def __deepcopy__(self, memo):
18 # Deep copy: copy value and deepcopy each child
19 new_children = [copy.deepcopy(child, memo) for child in self.children]
20 new = self.__class__(copy.deepcopy(self.value, memo), new_children)
21 memo[id(self)] = new
22 return new- Dengan mengimplementasikan
__copy__dan__deepcopy__, Anda dapat secara fleksibel mengendalikan perilaku penyalinan yang khusus untuk kelas. Sebagai contoh, Anda dapat menangani kasus di mana atribut tertentu harus merujuk (berbagi) objek yang sama, sementara atribut lain harus diduplikasi sebagai objek yang benar-benar baru. - Argumen
memoyang diteruskan ke__deepcopy__adalah sebuah kamus (dictionary) yang mencatat objek yang sudah diproses selama penyalinan rekursif dan mencegah loop tak berujung yang disebabkan oleh referensi melingkar.
1# Build tree
2root_node = Node('root', [Node('child1'), Node('child2')])
3
4shallow_copy = copy.copy(root_node) # shallow copy
5deep_copy = copy.deepcopy(root_node) # deep copy
6
7# Modify children
8# affects both root_node and shallow_copy
9shallow_copy.children.append(Node('child_shallow'))
10# affects deep_copy only
11deep_copy.children.append(Node('child_deep'))
12
13# Print results
14print("root_node:", root_node)
15print("shallow_copy:", shallow_copy)
16print("deep_copy:", deep_copy)- Dalam kode ini, variabel
shallow_copydibuat melalui 'shallow copy', sehingga hanya tingkat teratas dari objek yang diduplikasi, dan objek yang direferensikan secara internal (dalam hal ini, daftarchildren) dibagikan denganroot_nodeasli. Akibatnya, ketika Anda menambahkan node baru keshallow_copy, daftarchildrenyang dibagikan diperbarui dan perubahan tersebut juga tercermin dalam isiroot_node. - Di sisi lain, variabel
deep_copydibuat melalui 'deep copy', sehingga struktur internal diduplikasi secara rekursif, menghasilkan pohon yang sepenuhnya independen dariroot_node. Oleh karena itu, meskipun Anda menambahkan node baru kedeep_copy, hal itu tidak memengaruhiroot_node.
Referensi siklik (objek rekursif) dan pentingnya memo
Ketika graf objek yang kompleks mereferensikan dirinya sendiri (referensi siklik), deepcopy menggunakan dictionary memo untuk melacak objek yang sudah disalin dan mencegah loop tak berujung.
1# Example: recursive list and deepcopy memo demonstration
2import copy
3
4a = []
5b = [a]
6a.append(b) # a -> [b], b -> [a] (cycle)
7
8# deepcopy can handle cycles
9deep = copy.deepcopy(a)
10print("deep copy succeeded, length:", len(deep))- Secara internal,
deepcopymenggunakanmemountuk merujuk objek yang sudah diduplikasi, sehingga memungkinkan penyalinan yang aman bahkan ketika terdapat siklus. Anda memerlukan mekanisme serupa saat melakukan rekursi secara manual.
copyreg dan penyalinan kustom bergaya serialisasi (lanjutan)
Jika Anda ingin mendaftarkan perilaku penyalinan khusus yang terkoordinasi dengan pustaka, Anda dapat menggunakan modul standar copyreg untuk mendaftarkan bagaimana objek dikonstruksi (dibangun kembali). Ini berguna untuk objek kompleks dan tipe ekstensi C.
1# Example: using copyreg to customize pickling/copy behavior (brief example)
2import copy
3import copyreg
4
5class Wrapper:
6 def __init__(self, data):
7 self.data = data
8
9def reduce_wrapper(obj):
10 # Return callable and args so object can be reconstructed
11 return (Wrapper, (obj.data,))
12
13copyreg.pickle(Wrapper, reduce_wrapper)
14
15w = Wrapper([1, 2, 3])
16w_copy = copy.deepcopy(w)
17print("w_copy.data:", w_copy.data)- Dengan
copyreg, Anda dapat menyediakan aturan rekonstruksi yang memengaruhipickledandeepcopy. Ini adalah API tingkat lanjut, dan dalam banyak kasus__deepcopy__sudah mencukupi.
Peringatan dan jebakan praktis
Ada beberapa poin penting untuk memastikan perilaku yang benar saat menggunakan modul copy. Di bawah ini kami jelaskan jebakan umum yang mungkin Anda temui dalam pengembangan dan cara menghindarinya.
- Performa
deepcopydapat mengonsumsi memori dan CPU yang signifikan, jadi pertimbangkan dengan cermat apakah itu diperlukan. - Menangani elemen yang ingin Anda bagikan
Jika Anda ingin beberapa atribut, seperti cache besar, tetap dibagikan, secara sengaja hindari menyalin referensinya di dalam
__deepcopy__. - Keadaan internal immutabel Jika Anda menyimpan data immutabel di dalam, penyalinan mungkin tidak diperlukan.
- Thread dan sumber daya eksternal Sumber daya yang tidak dapat disalin, seperti soket dan handle berkas, tidak ada gunanya disalin atau akan menimbulkan error, sehingga Anda harus menghindari menyalinnya sejak tahap desain.
Contoh praktis: pola untuk memperbarui dictionary secara aman
Saat memperbarui objek konfigurasi yang kompleks, contoh ini menggunakan deepcopy untuk melindungi pengaturan asli.
1# Example: safely update a nested configuration using deepcopy
2import copy
3
4default_config = {
5 "db": {"host": "localhost", "ports": [5432]},
6 "features": {"use_cache": True, "cache_sizes": [128, 256]},
7}
8
9# Create a working copy to modify without touching defaults
10working = copy.deepcopy(default_config)
11working["db"]["ports"].append(5433)
12working["features"]["cache_sizes"][0] = 512
13
14print("default_config:", default_config)
15print("working:", working)Dengan deepcopy, Anda dapat dengan aman membuat konfigurasi turunan tanpa berisiko merusak pengaturan default. Ini sangat berguna untuk struktur bertingkat yang dapat diubah, seperti konfigurasi.
Praktik terbaik
Untuk menggunakan modul copy secara aman dan efektif, penting untuk mengingat pedoman praktis berikut.
- Pilih antara salinan dangkal dan mendalam berdasarkan apakah akan ada perubahan dan sejauh mana cakupannya.
- Implementasikan
__copy__dan__deepcopy__di kelas kustom untuk mencapai perilaku yang diharapkan. - Karena salinan mendalam mahal, kurangi kebutuhan penyalinan melalui desain bila memungkinkan. Pertimbangkan immutabilitas dan metode klon eksplisit, di antara teknik lainnya.
- Saat berhadapan dengan referensi siklik, manfaatkan
deepcopyatau sediakan mekanisme mirip memo secara manual. - Rancang agar sumber daya eksternal seperti handle file dan thread tidak disalin.
Kesimpulan
Modul copy adalah alat standar untuk duplikasi objek di Python. Dengan memahami perbedaan antara salinan dangkal dan mendalam dengan benar serta mengimplementasikan perilaku penyalinan kustom saat diperlukan, Anda dapat melakukan duplikasi secara aman dan dapat diprediksi. Dengan memperjelas pada tahap desain apakah penyalinan benar-benar diperlukan dan apa yang harus dibagikan, Anda dapat menghindari bug dan masalah performa yang tidak perlu.
Anda dapat mengikuti artikel di atas menggunakan Visual Studio Code di saluran YouTube kami. Silakan periksa juga saluran YouTube kami.