7. diel - Kopírovanie objektov v Pythone
V dnešnom tutoriále objektovo orientovaného programovania v Pythone si dôkladne vysvetlíme spôsoby, ktorými je možné vytvoriť kópie objektu. Ukážeme si ich výhody aj nevýhody a tiež riziká, ktoré sú s nimi spojené.
Kopírovanie objektov
V bežnom programovaní často pracujeme s objektmi a manipulujeme s nimi. To zahŕňa aj vytváranie ich kópií. Z predchádzajúcich lekcií už vieme, že keď máme dve referencie na rovnaký objekt a zmeníme jednu, druhá sa zmení tiež, pretože obe referencie ukazujú na rovnakú pamäťovú oblasť. Lenže v niektorých situáciách chceme, aby pôvodné dáta zostali nedotknuté, napríklad keď robíme nejaké dočasné zmeny pre analýzu alebo spracovanie dát. Mať kópiu objektu nám umožní vykonávať zmeny práve len na kópii, zatiaľ čo pôvodné dáta zostanú nezmenené. Nemusíme sa tiež obávať vedľajších efektov spojených so zdieľaním referencií na objekty medzi rôznymi časťami programu. Ako teda môžeme vytvoriť skutočnú kópiu objektu?
Možností, ako kopírovať objekty v Pythone, máme niekoľko:
- Kópia pomocou konštruktora - Vytvorí novú inštanciu objektu pomocou existujúceho konštruktora a odovzdanie potrebných atribútov.
- Plytká kópia pomocou funkcie
copy()
- Rýchle kopírovanie, ktoré vytvára nový objekt, ale v prípade zložených objektov (napr. zoznamov v zozname) kopíruje iba odkazy na vnútorné objekty. - Hlboká kópia pomocou funkcie
deepcopy()
- Vytvára nový objekt a rekurzívne kopíruje všetky vnútorné objekty. Výsledkom je úplne nezávislá kópia pôvodného objektu. - Kópia pomocou metódy
__repr__()
a funkcieeval()
- Metóda__repr__()
vracia textovú reprezentáciu objektu, ktorú je možné potom odovzdať funkciueval()
pre dynamické vytvorenie novej inštancie objektu.
Kópia pomocou konštruktora
Keď chceme vytvoriť kópiu objektu v Pythone, jedným z priamych spôsobov je využiť už existujúci konštruktor triedy. Toto je často intuitívna metóda, pretože ide o vytvorenie novej inštancie objektu s pôvodnými hodnotami.
Uveďme si príklad na základe našej triedy Kostka
. Ak máme
už existujúcu inštanciu s určitým počtom stien, dokážeme ľahko
vytvoriť novú inštanciu tejto triedy, odovzdať jej rovnaký počet stien a
tým pádom vytvoriť jej kópiu:
Vo výstupe uvidíme:
Kópiu inštancie
puvodni_kostka
sme vytvorili jednoducho tým, že sme znovu
vytvorili inštanciu triedy Kostka
a odovzdali ju počet stien z
pôvodného objektu. Výsledkom sú dva rôzne objekty s identickými hodnotami,
ale oba objekty sú na sebe úplne nezávislé.
Je dôležité si uvedomiť, že pri kopírovaní objektu týmto spôsobom musíme skopírovať všetky jeho atribúty, aby kópia bola naozaj kompletná a presne zodpovedala originálu.
Plytká kópia pomocou funkcie
copy()
Plytká kópia objektu vytvorí nový objekt, ale nekopíruje vnorené objekty, na ktoré pôvodný objekt odkazuje. V mnohých prípadoch môže byť plytká kópia dostačujúca, avšak je dôležité chápať jej obmedzenia.
V prípade našej triedy Kostka
, ktorá má iba základné
atribúty, bude plytká kópia fungovať bez problémov, pretože neexistujú
žiadne vnorené objekty na zdieľanie.
Tu je ukážka, ako vytvoriť plytkú kópiu objektu triedy
Kostka
pomocou funkcie copy()
z modulu
copy
:
Vo výstupe uvidíme:
Uveďme si ešte príklad s vnorenými objektmi:
Máme triedu Kostka
a triedu Hrac
, kde hráč bude
mať svoju kocku:
Teraz vytvoríme hráča s konkrétnou kockou:
Vo výstupe uvidíme:
Vytvoríme plytkú kópiu hráča a potom zmeníme počet stien. A áno, porušujeme tu pravidlo o privátnych atribútoch, ale len na účely objasnenia mechanizmu kópie:
Vo výstupe uvidíme:
Z výstupu je zrejmé, že aj
keď sme zmenili pôvodnú kocku až po vytvorení kópie hráča, zmenil sa
počet jej stien aj pri tejto kópii. Dôvodom je to, že plytká kópia síce
vytvorila nový objekt triedy Hrac
, ale vnorený objekt triedy
Kostka
zostal rovnaký a obaja hráči naň odkazujú.
Hlboká kópia pomocou funkcie
deepcopy()
Na vytvorenie kompletnej kópie objektu vrátane všetkých vnorených
objektov máme k dispozícii v module copy
funkciu
deepcopy()
. Táto funkcia prechádza celým objektom a rekurzívne
vytvára kópie všetkých vnorených objektov.
Pozrime sa na náš predchádzajúci príklad s hráčom a kockou:
Vo výstupe uvidíme:
Ako vidíme, zmena pôvodnej
kocky nijako neovplyvnila kocku pri kópii hráča. To je zásluha hlbokej
kópie, ktorá zaisťuje, že všetky vnorené objekty sú tiež skopírované,
a teda sú úplne nezávislé na origináloch. Tieto vlastnosti robia z
deepcopy()
najjednoduchší variant vytvorenia kópie objektu.
Pozor ale na fakt, že metóda deepcopy()
je veľmi
náročná na výkon.
Táto metóda je vhodná najmä v prípadoch, keď pracujeme s komplexnými objektmi s mnohými vnorenými atribútmi alebo objektmi, ktoré môžu byť menené v budúcnosti a chceme sa vyvarovať nežiaducim efektom.
Kópia pomocou metódy
__repr__()
a funkcie eval()
V Pythone existujú dve základné metódy na vytváranie textovej
reprezentácie objektov. Nám už dobre známa metóda __str__()
a
tiež zatiaľ tajomná metóda __repr__()
. Zatiaľ čo
__str__()
je určená pre "peknú" textovú reprezentáciu objektu
pre koncového používateľa, __repr__()
má za cieľ vytvoriť
"oficiálnu" textovú reprezentáciu objektu. Ako si to predstaviť? Metóda
__repr__()
vracia reťazec, ktorý po odovzdaní funkcie
eval()
vytvorí objekt, ktorý je ekvivalentný pôvodnému
objektu. Teda kópiu:-)
Tu vzniká otázka: je __repr__()
akýmsi "dumpom" časti
pamäte s objektom, uloženej v textovom reťazci? Odpoveď je nie. Namiesto
toho je to skôr kódový výraz, ktorý, keď je vyhodnotený, vráti novú
inštanciu rovnakého objektu s rovnakými dátami. Majme teda triedu, ktorej
__repr__()
metóda vráti kód potrebný na vytvorenie novej
inštancie s tými istými dátami. Funkcia eval()
potom vytvorí
jej novú inštanciu. Výsledný objekt bude mať rovnaké metódy, pretože je
stále inštanciou rovnakej triedy.
Ukážme si príklad:
Vo výstupe uvidíme:
Metódu
__repr__()
sme pridali tak, aby reprezentovala inštanciu triedy
Kostka
vo formáte, ktorý by bolo možné použiť na opätovné
vytvorenie rovnakej inštancie. V našom prípade metóda
__repr__()
vracia reťazec vo formáte
Kostka(pocet_sten)
, kde pocet_sten
je aktuálny počet
stien kocky.
Táto metóda kopírovania sa zameriava predovšetkým na dáta a nie na zložité správanie objektu alebo vnorené objekty.
Treba však mať na pamäti, že funkcia eval()
je zo svojej
podstaty (tvorba kódu z reťazca) riziková a ľahko sa stane zdrojom vážnych
bezpečnostných problémov.
Nikdy preto nepoužívajme funkciu eval()
na
dátach, pri ktorých si nie sme istí, že pochádza z overených zdrojov.
To je pre túto lekciu všetko:)
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 34x (3.46 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Python