Modul `copy` Python

Modul `copy` Python

Artikel ini menerangkan modul copy Python.

Dengan menumpukan pada perbezaan antara salinan cetek dan salinan mendalam, kami memberikan penjelasan yang jelas—daripada mekanisme asas penduaan objek hinggalah aplikasi dalam kelas tersuai—berserta contoh praktikal.

YouTube Video

Modul copy Python

Modul copy Python ialah modul piawai untuk mengendalikan penduaan objek (penyalinan). Matlamatnya ialah memahami perbezaan antara salinan cetek dan salinan mendalam serta mampu mengawal tingkah laku penyalinan bagi objek tersuai.

Asas: Apakah itu salinan cetek?

Di sini kami menggambarkan kelakuan salinan cetek bagi objek boleh ubah seperti senarai dan kamus. Salinan cetek hanya menduplikasi objek aras teratas dan berkongsi rujukan 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 kod ini, senarai aras teratas digandakan, tetapi subsenarai dalaman dikongsi melalui rujukan, jadi shallow[1].append(99) juga tercermin dalam original. Ini ialah kelakuan tipikal salinan cetek.

Apakah itu salinan mendalam?

Salinan mendalam menduplikasi objek dan semua rujukan dalamannya secara rekursif, menghasilkan objek yang bebas. Gunakannya apabila anda mahu menduplikasi struktur bersarang yang kompleks dengan selamat.

 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)

Dalam contoh ini, perubahan pada deep tidak menjejaskan original. deepcopy menyalin keseluruhan graf objek, menghasilkan replika yang bebas.

Cara menentukan yang mana hendak digunakan

Pemilihan jenis salinan hendaklah ditentukan oleh struktur objek dan tujuan anda. Jika anda tidak mahu perubahan pada unsur dalaman yang boleh ubah menjejaskan objek asal, deepcopy adalah sesuai. Ini kerana ia menduplikasi keseluruhan objek secara rekursif, menghasilkan salinan yang benar-benar bebas.

Sebaliknya, jika menduplikasi hanya objek aras teratas sudah memadai dan anda mementingkan kelajuan serta kecekapan memori, copy (salinan cetek) lebih sesuai.

  • Anda mahu mengelakkan perubahan pada asal apabila anda mengubah unsur dalaman yang boleh ubah → gunakan deepcopy.
  • Menduplikasi hanya aras teratas sudah memadai dan anda mahu mengutamakan prestasi (kelajuan/memori) → gunakan copy (salinan cetek).

Mengendalikan objek terbina dalam dan tidak boleh ubah

Objek tidak boleh ubah (cth., int, str, dan tuple yang kandungannya tidak boleh ubah) lazimnya tidak perlu disalin. copy.copy mungkin memulangkan objek yang sama apabila 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)

Oleh kerana menyalin objek tidak boleh ubah memberikan manfaat kecekapan yang kecil, objek yang sama mungkin digunakan semula. Ini jarang sekali perlu dibimbangkan dalam reka bentuk aplikasi.

Kelas tersuai: mentakrifkan __copy__ dan __deepcopy__

Jika penyalinan lalai tidak seperti yang anda jangkakan untuk kelas anda, anda boleh menyediakan logik salinan anda sendiri. Jika anda mentakrifkan __copy__ dan __deepcopy__, 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 melaksanakan __copy__ dan __deepcopy__, anda boleh mengawal secara fleksibel kelakuan penyalinan khusus kelas. Sebagai contoh, anda boleh mengendalikan kes di mana atribut tertentu harus merujuk kepada (berkongsi) objek yang sama, manakala atribut lain harus diduplikasi sebagai objek baharu yang sepenuhnya.
  • Argumen memo yang dihantar kepada __deepcopy__ ialah kamus yang merekodkan objek yang telah diproses semasa penyalinan rekursif dan mengelakkan gelung tak terhingga yang disebabkan oleh rujukan kitaran.
 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 kod ini, pembolehubah shallow_copy dicipta melalui 'salinan cetek', jadi hanya aras teratas objek yang digandakan, dan objek yang dirujuk di dalamnya (dalam kes ini, senarai children) dikongsi dengan root_node asal. Akibatnya, apabila anda menambah nod baharu ke shallow_copy, senarai children yang dikongsi akan dikemas kini dan perubahan itu turut tercermin pada kandungan root_node.
  • Sebaliknya, pembolehubah deep_copy dicipta melalui 'salinan mendalam', jadi struktur dalaman digandakan secara rekursif, menghasilkan pokok yang benar-benar bebas daripada root_node. Oleh itu, walaupun anda menambah nod baharu pada deep_copy, ia tidak menjejaskan root_node.

Rujukan kitaran (objek rekursif) dan kepentingan memo

Apabila graf objek yang kompleks merujuk dirinya sendiri (rujukan kitaran), deepcopy menggunakan kamus memo untuk menjejak objek yang telah disalin dan mengelakkan gelung tak terhingga.

 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 dalaman, deepcopy menggunakan memo untuk merujuk objek yang telah diduplikasi, membolehkan penyalinan yang selamat walaupun wujud kitaran. Anda memerlukan mekanisme yang serupa apabila melakukan rekursi secara manual.

copyreg dan penyalinan tersuai gaya serialisasi (lanjutan)

Jika anda mahu mendaftarkan tingkah laku penyalinan khas selaras dengan pustaka, anda boleh menggunakan modul piawai copyreg untuk mendaftarkan cara objek dibina (semula). Ini berguna untuk objek kompleks dan jenis peluasan 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 menggunakan copyreg, anda boleh menyediakan peraturan pembinaan semula yang mempengaruhi kedua-dua pickle dan deepcopy. Ia ialah API lanjutan, dan dalam kebanyakan kes __deepcopy__ sudah memadai.

Amaran dan perangkap praktikal

Terdapat beberapa perkara penting untuk memastikan kelakuan yang betul apabila menggunakan modul copy. Di bawah, kami menerangkan perangkap lazim yang mungkin anda temui dalam pembangunan dan cara mengelakkannya.

  • Prestasi deepcopy boleh menggunakan memori dan CPU yang ketara, jadi pertimbangkan dengan teliti sama ada ia benar-benar diperlukan.
  • Mengendalikan unsur yang anda mahu kongsi Jika anda mahu sesetengah atribut, seperti cache yang besar, kekal dikongsi, elakkan dengan sengaja daripada menyalin rujukannya dalam __deepcopy__.
  • Keadaan dalaman yang tidak boleh ubah Jika anda menyimpan data yang tidak boleh ubah secara dalaman, penyalinan mungkin tidak perlu.
  • Thread dan sumber luaran Sumber yang tidak boleh disalin, seperti soket dan pemegang fail, sama ada tidak bermakna untuk disalin atau akan menyebabkan ralat, jadi anda mesti mengelakkan daripada menyalinnya melalui reka bentuk.

Contoh praktikal: corak untuk mengemas kini kamus dengan selamat

Apabila mengemas kini objek konfigurasi yang kompleks, contoh ini menggunakan deepcopy untuk melindungi tetapan asal.

 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 menggunakan deepcopy, anda boleh mencipta konfigurasi terbitan dengan selamat tanpa berisiko merosakkan tetapan lalai. Ini amat berguna untuk struktur bersarang yang boleh ubah seperti konfigurasi.

Amalan terbaik

Untuk menggunakan modul copy dengan selamat dan berkesan, adalah penting untuk mengingati garis panduan praktikal berikut.

  • Pilih antara salinan cetek dan salinan mendalam berdasarkan sama ada perubahan akan berlaku dan skopnya.
  • Laksanakan __copy__ dan __deepcopy__ dalam kelas tersuai untuk mencapai kelakuan yang dijangka.
  • Oleh kerana salinan mendalam adalah mahal, kurangkan keperluan penyalinan melalui reka bentuk apabila boleh. Pertimbangkan ketidakbolehubahan dan kaedah klon eksplisit, antara teknik lain.
  • Apabila berurusan dengan rujukan kitaran, sama ada manfaatkan deepcopy atau sediakan mekanisme seakan memo secara manual.
  • Reka bentuk supaya sumber luaran seperti pengendali fail dan utas tidak disalin.

Kesimpulan

Modul copy ialah alat piawai untuk penduaan objek dalam Python. Dengan memahami perbezaan antara salinan cetek dan salinan mendalam dengan betul serta melaksanakan tingkah laku penyalinan tersuai apabila diperlukan, anda boleh melakukan penduaan dengan selamat dan boleh diramal. Dengan menjelaskan pada peringkat reka bentuk sama ada penyalinan benar-benar perlu dan apa yang patut dikongsi, anda boleh mengelakkan pepijat dan isu prestasi yang tidak perlu.

Anda boleh mengikuti artikel di atas menggunakan Visual Studio Code di saluran YouTube kami. Sila lihat juga saluran YouTube kami.

YouTube Video