Python `copy` Module
Ipapaliwanag ng artikulong ito ang copy module ng Python.
Sa pagtutok sa pagkakaiba ng mababaw at malalim na kopya, nagbibigay kami ng malinaw na paliwanag—mula sa mga batayang mekanismo ng pagdodoble ng object hanggang sa mga aplikasyon sa pasadyang klase—kasama ang mga praktikal na halimbawa.
YouTube Video
Python copy Module
Ang copy module ng Python ang pamantayang module para sa paghawak ng pagdodoble (pagkopya) ng mga object. Ang layunin ay maunawaan ang pagkakaiba ng mababaw at malalim na kopya at makontrol ang pag-uugali ng pagkopya para sa mga pasadyang object.
Mga batayan: Ano ang mababaw na kopya?
Dito ay inilalarawan namin ang pag-uugali ng mababaw na kopya para sa mga nababagong (mutable) object gaya ng mga list at diksyunaryo. Ang mababaw na kopya ay nagdodoble lamang ng object sa pinakaibabaw na antas at ibinabahagi ang mga sanggunian (references) sa loob.
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)Sa code na ito, nadodoble ang listahan sa pinakamataas na antas, ngunit ang mga panloob na sublist ay ibinabahagi sa pamamagitan ng reference, kaya ang shallow[1].append(99) ay makikita rin sa original. Ito ay tipikal na pag-uugali ng isang mababaw na kopya.
Ano ang malalim na kopya?
Ang malalim na kopya ay paulit-ulit (recursively) na nagdodoble ng object at lahat ng panloob nitong sanggunian, na lumilikha ng isang independiyenteng object. Gamitin ito kapag nais mong ligtas na madoble ang mga komplikadong nested na istruktura.
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)Sa halimbawang ito, ang mga pagbabago sa deep ay hindi nakaaapekto sa original. Kinokopya ng deepcopy ang buong graph ng object, na nagbubunga ng isang independiyenteng replika.
Paano pipili kung alin ang gagamitin
Ang pagpili kung anong kopya ang gagamitin ay dapat ibatay sa estruktura ng object at sa iyong layunin. Kung ayaw mong makaapekto sa orihinal na object ang mga pagbabagong gagawin sa panloob na mga nababagong (mutable) elemento, ang deepcopy ang nararapat. Dahil paulit-ulit nitong dinodoble ang buong object, lumilikha ito ng ganap na independiyenteng kopya.
Sa kabilang banda, kung sapat na ang pagdodoble lamang ng object sa pinakaibabaw na antas at pinahahalagahan mo ang bilis at episyensya sa memorya, mas angkop ang copy (mababaw na kopya).
- Nais mong maiwasan na magbago ang orihinal kapag binago mo ang panloob na mga nababagong elemento → gumamit ng
deepcopy. - Sapat na ang pagdodoble sa pinakaibabaw na antas at nais mong unahin ang performance (bilis/memorya) → gumamit ng
copy(mababaw na kopya).
Paghawak sa mga built-in at immutable na object
Ang mga immutable na object (hal., int, str, at mga tuple na ang nilalaman ay di-nababago) ay karaniwang hindi kailangang kopyahin. Maaaring ibalik ng copy.copy ang parehong object kapag naaangkop.
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)Dahil kaunti lang ang benepisyo sa episyensya ng pagkopya ng mga immutable na object, maaaring muling gamitin ang parehong object. Bihira itong bagay na dapat mong ikabahala sa disenyo ng aplikasyon.
Mga pasadyang klase: pagde-define ng __copy__ at __deepcopy__
Kung ang default na pagkopya ay hindi ayon sa inaasahan para sa iyong klase, maaari kang magbigay ng sarili mong lohika ng pagkopya. Kapag dinefine mo ang __copy__ at __deepcopy__, gagamitin ang mga ito ng copy.copy at copy.deepcopy.
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- Sa pagpapatupad ng
__copy__at__deepcopy__, maaari mong kontrolin nang may kakayahang umangkop ang pag-uugali ng pagkopya na partikular sa klase. Halimbawa, maaari mong pangasiwaan ang mga kaso kung saan ang ilang attribute ay dapat tumukoy sa (magbahagi ng) iisang object, samantalang ang iba pang mga attribute ay dapat kopyahin bilang ganap na mga bagong object. - Ang argumentong
memona ipinapasa sa__deepcopy__ay isang diksyunaryo na nagtatala ng mga object na naproseso na sa panahon ng recursive na pagkopya at pumipigil sa mga walang hanggang loop na dulot ng mga circular reference.
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)- Sa code na ito, ang variable na
shallow_copyay nalilikha sa pamamagitan ng 'shallow copy', kaya tanging ang pinakamataas na antas ng object lamang ang nadodoble, at ang mga object na tinutukoy nito sa loob (sa kasong ito, ang listahangchildren) ay ibinabahagi sa orihinal naroot_node. Bilang resulta, kapag nagdagdag ka ng bagong node sashallow_copy, naa-update ang ibinahaging listahangchildrenat ang pagbabago ay makikita rin sa nilalaman ngroot_node. - Sa kabilang banda, ang variable na
deep_copyay nalilikha sa pamamagitan ng 'deep copy', kaya ang panloob na istruktura ay nadodoble nang paulit-ulit (recursively), na lumilikha ng isang puno na ganap na hiwalay saroot_node. Kaya, kahit magdagdag ka ng bagong node sadeep_copy, hindi nito naaapektuhan angroot_node.
Mga paikot na sanggunian (recursive na mga object) at ang kahalagahan ng memo
Kapag ang isang masalimuot na graph ng object ay tumutukoy sa sarili nito (paikot na sanggunian), gumagamit ang deepcopy ng memo na dictionary upang subaybayan ang mga object na nakopya na at maiwasan ang walang-hanggang pag-ikot.
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))- Sa loob, gumagamit ang
deepcopyngmemoupang tumukoy sa mga object na nadoble na, na nagbibigay-daan sa ligtas na pagkopya kahit may mga cycle. Kailangan mo ng kaparehong mekanismo kapag mano-manong nagsasagawa ng recursion.
copyreg at pasadyang pagkopya na may estilong serialisasyon (advanced)
Kung nais mong irehistro ang espesyal na gawi ng pagkopya kasabay ng mga library, maaari mong gamitin ang pamantayang module na copyreg upang irehistro kung paano (muli) nabubuo ang mga object. Kapaki-pakinabang ito para sa masalimuot na mga object at mga uri ng C extension.
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)- Sa paggamit ng
copyreg, maaari kang magbigay ng mga tuntunin sa muling pagbubuo na nakaaapekto sa parehongpickleatdeepcopy. Ito ay isang advanced na API, at sa karamihan ng kaso sapat na ang__deepcopy__.
Mga praktikal na babala at karaniwang pagkakamali
May ilang mahahalagang punto upang matiyak ang tamang pag-uugali kapag gumagamit ng copy module. Ipinaliliwanag sa ibaba ang mga karaniwang pagkakamaling maaari mong makaharap sa development at kung paano iwasan ang mga ito.
- Pagganap
Maaaring gumamit ang
deepcopyng malaking memorya at CPU, kaya pag-isipang mabuti kung talagang kailangan ito. - Paghawak sa mga elementong nais mong ibahagi
Kung nais mong manatiling shared ang ilang attribute, gaya ng malalaking cache, sadyang iwasan ang pagkopya ng kanilang mga reference sa loob ng
__deepcopy__. - Di-nababagong panloob na estado Kung may hawak kang di-nababagong data sa loob, maaaring hindi na kailangan ang pagkopya.
- Mga thread at panlabas na resource Ang mga resource na hindi maaaring kopyahin, gaya ng mga socket at file handle, ay alinman ay walang saysay kopyahin o magdudulot ng mga error, kaya dapat mong iwasan ang pagkopya sa kanila sa disenyo pa lang.
Praktikal na halimbawa: isang pattern para sa ligtas na pag-update ng mga diksyunaryo
Kapag ina-update ang isang masalimuot na configuration object, ginagamit sa halimbawang ito ang deepcopy upang protektahan ang orihinal na mga setting.
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)Gamit ang deepcopy, maaari kang ligtas na lumikha ng mga hinangong configuration nang hindi isinusugal ang pagkasira ng default na mga setting. Ito ay lalo nang kapaki-pakinabang para sa mga naka-nest na nababagong istruktura gaya ng mga configuration.
Mga pinakamainam na gawi
Upang magamit nang ligtas at epektibo ang module na copy, mahalagang tandaan ang mga sumusunod na praktikal na gabay.
- Pumili sa pagitan ng mababaw at malalim na kopya batay sa kung may magaganap na mga pagbabago at sa lawak ng mga ito.
- Ipatupad ang
__copy__at__deepcopy__sa mga pasadyang klase upang makamit ang inaasahang pag-uugali. - Dahil magastos ang malalim na kopya, bawasan ang pangangailangan sa pagkopya sa pamamagitan ng disenyo kung maaari. Isaalang-alang ang pagiging immutable at mga tahasang clone method, bukod sa iba pang teknik.
- Kapag humahawak ng mga paikot na sanggunian, alinman ay gamitin ang
deepcopyo magbigay ng mekanismong kawangis ng memo nang mano-mano. - Idisenyo nang hindi nakokopya ang mga panlabas na resource gaya ng mga file handle at mga thread.
Konklusyon
Ang copy module ang pamantayang kasangkapan para sa pagdodoble ng object sa Python. Sa wastong pag-unawa sa mga pagkakaiba ng mababaw at malalim na kopya at sa pagpapatupad ng pasadyang gawi sa pagkopya kapag kailangan, maaari mong maisagawa ang pagdodoble nang ligtas at mahuhulaan. Sa paglinaw sa yugto ng disenyo kung talagang kailangan ang pagkopya at kung ano ang dapat ibahagi, maiiwasan mo ang hindi kinakailangang mga bug at mga isyu sa pagganap.
Maaari mong sundan ang artikulo sa itaas gamit ang Visual Studio Code sa aming YouTube channel. Paki-check din ang aming YouTube channel.