Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

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 funkcie eval() - Metóda __repr__() vracia textovú reprezentáciu objektu, ktorú je možné potom odovzdať funkciu eval() pre dynamické vytvorenie novej inštancie objektu.
V nasledujúcich kapitolách lekcie sa pozrieme na každú z týchto možností podrobnejšie, aby sme lepšie pochopili ich funkciu a vhodné použitie.

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

 

Predchádzajúci článok
Bojovník do arény v Pythone
Všetky články v sekcii
Objektovo orientované programovanie v Pythone
Preskočiť článok
(neodporúčame)
Dedičnosť a polymorfizmus v Pythone
Článok pre vás napísal Karel Zaoral
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Karel Zaoral
Aktivity