Ez a cikk kiemeli azokat a Python speciális módszereket, amelyeket minden Python programozónak ismernie kell
A mágikus módszerek segítségével gazdagíthatjuk alkalmazásainkat. Közvetett módon varázslatot adnak a Python kódunkhoz. Ez egy haladó szintű téma Python fejlesztők számára, és mindenkinek ajánlom, aki a Python programozási nyelvet használja/használni szándékozik.
A mágikus metódusok segítségével nagyobb kontrollt kapunk az alkalmazásunk viselkedése felett.
Ez a cikk célja, hogy elmagyarázza, mik azok a mágikus metódusok, és hogyan használhatjuk őket Python alkalmazások készítéséhez. Áttekintést nyújt a legelterjedtebb mágikus módszerekről egy sor adattípus esetében.
A legfontosabb mágikus módszerek felvázolásának célja, hogy megértsük, akarjuk-e felülírni ezeket a módszereket az egyéni osztályainkban, hogy gazdagítsuk az alkalmazásokat.
A mágikus módszerek rendkívül erőssé teszik a Python programozási nyelvet
Melyek a Python mágikus módszerek?
A python mágikus módszereket speciális módszereknek vagy dunder módszereknek is nevezik. Dupla aláhúzással vannak körülvéve pl. __init__().
Egy objektumnak több mágikus metódusa is lehet.
Ne feledjük, hogy Pythonban minden objektum, beleértve a változót/függvényt/osztályt stb. is. Az objektumok a python absztrakciója az adatoknak.
A mágikus metódusok új objektumok létrehozására és inicializálására szolgálnak, segítenek egy objektum szótárként való visszakeresésében, egy objektum törlésére szolgálnak többek között. Ezeket használjuk a + operátor meghívásakor, vagy akár akkor is, ha egy objektumot karakterláncként akarunk ábrázolni.
Noha a Pythonban minden metódus nyilvános, a kódolási konvenció szerint az összes privát metódust kettős aláhúzással __<method>__()
Ez azt jelenti, hogy a mágikus metódusokat privát metódusoknak szánjuk. Ez azt is jelenti, hogy az objektum hívójának nem szabad közvetlenül meghívnia a metódust, mivel a metódust annak az osztálynak kell meghívnia belsőleg, amelyik a mágikus metódussal rendelkezik.
A mágikus metódusokat felülírhatjuk, hogy saját egyéni funkcionalitást biztosítsunk.
Egy saját osztály létrehozásával ismertetem a mágikus módszerek fogalmát, majd áttekintést adok a legfontosabb mágikus módszerekről egy egész szám, string, lista és szótár esetében.
Amint haladunk előre a cikkben, elkezd sokkal világosabb képet alkotni arról, hogy miért léteznek a mágikus metódusok, és hogyan használjuk őket.
Ha szeretné megérteni a Python programozási nyelvet a kezdőtől a haladó szintig, akkor nagyon ajánlom az alábbi cikket:
A mágikus módszerek témáját egy saját osztály létrehozásával kezdem, majd elmagyarázom, hogyan használják a mágikus módszereket.
A mágikus módszerek megértéséhez szükséges felhasználási eset
A lenti részletben létrehoztam egy Human nevű osztályt, majd példányosítottam az Human osztály egy példányát.
Ezt a kódrészletet fogjuk használni a mágikus módszerek megértéséhez.
Megjegyezzük, hogy a human objektum id értéke integer típusú, a name attribútum string típusú, a property addresses list típusú, a property maps pedig dictionary típusú.
A mágikus módszereket a könnyebb olvashatóság érdekében különböző adattípusokba csoportosítottam. Azonban ugyanazok a mágikus módszerek különböző adattípusokban is megtalálhatók.
A mágikus módszerek felülbírálhatók a funkciók gazdagítása és az üzleti igényeknek leginkább megfelelő egyéni logika létrehozása érdekében.
Class:
Magyarázzuk meg az emberi objektumhoz tartozó mágikus módszereket. Ha végrehajtom a dir(human) metódust, akkor a program felsorolja a human objektum összes függvényét és attribútumnevét.
Ez összesen 29 olyan metódus/attribútum van, amely a human objektumhoz kapcsolódik. Ezek közül 26 a mágikus metódus.
Ez elég sok speciális metódus. Ezek a metódusok az Ember osztály alaptípusából öröklődnek. Ennélfogva ezek az előre definiált metódusok, amelyeket használhatunk/felülbírálhatunk az osztályok gazdagítására.
A következő részben a legfontosabb mágikus metódusokat ismertetjük.
__delattr__
Ezt a metódust akkor hívjuk meg, amikor egy attribútumot próbálunk törölni egy osztályból.
A funkcionalitást felülbírálhatjuk, ha a metódust a Human osztályban implementáljuk:
def __delattr__(self, item):
print('Deleting attribute ' + item)
return object.__delattr__(self, item)
Most, amikor megpróbálunk törölni egy attribútumot, megjelenik az üzenet: Attribútum törlése
Létezik még a __setattr__() metódus, amellyel értéket rendelhetünk egy attribútumhoz, és a __getattr__(), amellyel értéket kaphatunk az attribútumból.
A__delattr__() felhasználási esete lehet, hogy megakadályozzuk egy objektum bizonyos attribútumainak törlését, vagy amikor egy adott attribútum törlésekor bizonyos műveleteket akarunk végrehajtani.
__dict__
Ez a metódus egy szótárat ad vissza, amely az objektumot reprezentálja. A szótár kulcsai az objektum attribútumai, értékei pedig az attribútumok értékei.
Példányként:
human = Human(2, 'Malik').__dict__
print(human)
A fenti kód visszaadja:
{‘id’: 2, ‘name’: ‘Malik’, ‘addresses’: , ‘maps’: {}}
__dir__
A dir() metódust felülírhatjuk az osztály __dir__() metódusának felülírásával. Példának okáért a dir() metódus által visszaadott eredményből eltávolíthatjuk a belső metódusokat:
__eq__
Ezt a metódust akkor hívjuk meg, amikor a == műveletet próbáljuk végrehajtani. Tekintsük úgy, hogy két emberi objektum akkor egyenlő, ha az id attribútumuk akkor is egyenlő, ha a nevük különböző.
Az __eq__() metódust felülírhatjuk, hogy elérjük ezt a funkciót:
def __eq__(self, other):
return self.id == other.id
Ez most True-t fog visszaadni:
first = Human(1, 'Farhad')
second = Human(1, 'Malik')
print(first == second)
__format__
Amikor megpróbálunk stringet csinálni.format(), a __format__() metódust hívjuk meg belsőleg.
__ge__
Tegyük fel, hogy van két Human objektum:
first = Human(1, 'Farhad')
second = Human(2, 'Malik')
Tegyük fel azt is, hogy a projektünkben van egy olyan szabály, hogy a nagyobb Id-vel rendelkező Human objektumot nagyobbnak tekintjük, mint a másik Human objektumot. Ezért felülírhatjuk az __gt__() metódust, és megvalósíthatjuk az egyéni logikát:
def __ge__(self, other):
return self.id >= other.id
Ez most False-t fog visszaadni, mert a második emberi objektum Id-je nagyobb, mint az első emberi objektumé:
print(first >= second)
Returns False
Létezik egy __lt__() metódus is, amely a ≤ operátor végrehajtásakor végrehajtódik.
__hash__
A metódus az objektum egész számmá alakítására szolgál. A hashelés akkor történik, amikor egy szótárban/készletben egy elemet próbálunk beállítani.
A jó hashing algoritmus kevesebb hashing ütközést eredményez. A __hash__() metódus felülbírálásával saját hash algoritmust adhatunk meg.
Tegyük fel, hogy a Human objektum azonosítójának az alkalmazásunkban egyedinek kell lennie. Az __hash__() algoritmust felülbírálhatjuk, hogy a self.id-t hash egész számként adjuk vissza:
def __hash__(self):
return self.id
Létrehozhatunk két objektumot, és elmenthetjük őket egy halmazgyűjteménybe. Amikor lekérdezzük a halmaz hosszát, két elemet fogunk várni a halmazban, mert mindkét objektumnak más az id-je.
first = Human(1, 'Farhad')
second = Human(2, 'Malik')
my_set = set()
print(len(my_set))
#returns 2
Ha most az id-t mindkét objektum esetében 1-nek állítjuk be, és megismételjük a gyakorlatot, akkor csak 1 elemet fogunk látni a halmazban, mert mindkét objektumnak ugyanaz a hash-kulcsa, mivel az id attribútumuk azonos, még ha a name attribútumuk különböző is.
first = Human(1, 'Farhad')
second = Human(1, 'Malik')
my_set = set()
print(len(my_set))
#returns 1
__init__
A __init__() metódust akkor hajtjuk végre, amikor egy osztály új példányát akarjuk példányosítani az osztály konstruktorának meghívásával.
Az __init__() metódus végrehajtása során, amikor megpróbáltuk végrehajtani:
human = Human(1, 'farhad')
Az __init__() metódust hajtotta végre.
A funkciót felülírhatjuk, és átadhatjuk benne a saját egyéni argumentumainkat és viselkedésünket is.
A Human osztály __init__() metódusa példának okáért:
def __init__(self, id, name, addresses=, maps={}):
self.id = id
self.name = name
self.addresses = addresses
self.maps = maps
__init_subclass__
Ez az egyik meta-osztály felhasználási módja. Amikor egy osztályt alosztályozunk, és létrehozzuk az objektumát, akkor meghívjuk a __init_subclass__() metódust.
Lényegében a metódus tájékoztatja a szülőt, hogy alosztályozták. Ez a kampó ezután egy adott osztály összes alosztályát inicializálni tudja. Ezért a metódus az alosztályok regisztrálására és az alosztályok attribútumainak alapértelmezett értékek hozzárendelésére szolgál. Ezért lehetővé teszi számunkra, hogy testre szabjuk az alosztályok inicializálását.
A következő cikkemben elmagyarázom, hogyan működnek a metaosztályok.
__new__
Amikor egy osztály új példányát akarjuk példányosítani/alkotni, akkor a __new__(cls) metódust hajtjuk végre.
Elképzelésként tekintsük, hogy a Human() konstruktor meghívásakor ki akarjuk írni, hogy ‘Human creating…’.
Az __new__(cls) metódus működését az alábbiak szerint felülbírálhatjuk:
def __new__(cls, *args, **kwargs):
print('Human creating...')
return object.__new__(cls)
Az eredmény: Human creating… kiíródik, amikor megpróbáltunk létrehozni egy példányt:
human = Human(1, 'Farhad', , {'London':2, 'UK':3})
__sizeof__
Ezt a metódust a sys.getsizeof() végrehajtásakor hívjuk meg. Visszaadja az objektum méretét a memóriában, bájtokban.
__str__
Az __str__() függvénynek meg kell próbálnia az objektum felhasználóbarát ábrázolását visszaadni.
__weakref__
Az __weakref__ objektum a célobjektum gyenge hivatkozásainak listáját adja vissza. Lényegében a szemétgyűjtésnek segít tájékoztatni a gyenge referenciákat, hogy a referenciát begyűjtötték. Ezért megakadályozza, hogy az objektumok hozzáférjenek a mögöttes mutatókhoz.
__getnewargs__
A Pythonban időnként objektumokat pácolunk. A pácolás egy objektum bájtfolyamszerű reprezentációját hozza létre a memóriában, amely szükség esetén lemezre menthető.
A __getnewargs__() metódus tájékoztatja a pácolás folyamatát arról, hogyan kell visszatöltenie a pácolt objektumot, hogy újra létrehozza a célobjektumot. Különösen azt, hogy hogyan kell létrehozni az objektumot az argumentumok átadásával a new() metódusnak. Ezért a ‘get new args’ elnevezés.
__index__
Az __index__() metódus végrehajtásával egy objektum egész számmá alakítható. A metódust felül is írhatjuk, és megadhatjuk a saját funkcionalitásunkat, hogy hogyan kell az indexet létrehozni.
__invert__
Ezt a metódust akkor hajtjuk végre, ha a ~ operátort használjuk.
Példányként:
first = Human(1, 'Farhad')
print(~first.id)
Ez ugyanaz, mintha végrehajtanánk:
first = Human(1, 'Farhad')
print(first.id.__invert__())
__lshift__
Ez a metódus egy érték balra tolását adja nekünk pl. self << érték. A << operátort a __lshift__() metódus felülbírálásával tudjuk túlterhelni.
Megjegyzés: A __rshift__() akkor hajtódik végre, amikor a >> operátort hajtjuk végre.
__mod__
Ezt a metódust akkor hívjuk meg, amikor a % operátort használjuk.
__neg__
Ezt a metódust akkor hajtjuk végre, amikor a negatív – operátort használjuk pl.
first.id — second.id
__subclasshook__
Ezt a metódust felülírhatjuk az issubclass() metódus testre szabásához. Lényegében True-t ad vissza, ha egy osztály alosztály, és False-t, ha nem az. A metódus NotImplemented-t is visszaad, ami lehetővé teszi a meglévő algoritmus használatát.
A metódus testre szabhatja az issubclass() metódus eredményét.
String
Ezzel elérkeztünk a cikk következő részéhez.
Láthatjuk, hogy a human objektum name tulajdonsága string típusú. Ha ezután dir(human.name) műveletet végzünk a tulajdonságon, és kiszűrjük a dupla aláhúzással körülvett metódusokat, akkor észrevesszük, hogy összesen 33 mágikus metódus van.
print(len(list(filter(lambda x: x.startswith('__') and x.endswith('__'), dir(human1.name)))))
A 4 legfontosabb metódust ismertetem itt, mivel a cikk többi részében a mágikus metódusok nagy részét már kiemeltük.
__contains__
Ezt a metódust akkor hajtjuk végre, amikor megpróbáljuk ellenőrizni, hogy egy adott karakter létezik-e.
__len__
Ez a metódus a karakterlánc hosszát adja vissza. Akkor hajtódik végre, amikor a len() metódust végrehajtjuk.
Ha csak bizonyos karaktereket akarunk számolni a karakterlánc hosszának kiszámításához, akkor a __()__ metódus felülírható, hogy ezt a funkciót biztosítsa.
__repr__
Ezt a metódust akkor hajtjuk végre, ha egy objektum fejlesztőbarát reprezentációját szeretnénk létrehozni.
Megjegyezzük, hogy a __repr__ akkor hajtódik végre, amikor print(object), ha nincs __str__() implementáció az osztályunkban.
def __repr__(self):
return f'id={self.id} ({type(self.id)}). name={self.name} ({type(self.name)})'print(Human(1, 'Farhad'))
Ez kiírja:
id=1 (<class ‘int’>). name=Farhad (<class ‘str’>)
Az __repr__() metódusnak egy objektum hivatalos reprezentációjának előállítására kell szolgálnia.
__iadd__
Az e hozzárendelés mellett esetenként egy összeadási operátort is használunk.g.
self.id += 1
Ez egyenértékű a self.id = self.id + 1
Az iadd() metódus akkor kerül végrehajtásra, amikor az összeadást a hozzárendeléssel együtt végezzük.
Ezeken kívül az __ipow__() metódus akkor kerül végrehajtásra, amikor **=.
A __iand__() varázsmódszer a bitenkénti ÉS elvégzésére szolgál hozzárendeléssel, a __ior__() pedig akkor hívódik meg, amikor !=-ot próbálunk csinálni, például: i != j
List
Ezzel elérkeztünk a cikk következő részéhez. Az emberi objektum címek tulajdonsága lista típusú. Ha ezután dir(human.addresses) műveletet végzünk a címek tulajdonságon, és kiszűrjük a dupla aláhúzással körülvett metódusokat, akkor azzal fogunk találkozni, hogy összesen 35 mágikus metódus van.
print(len(list(filter(lambda x: x.startswith('__') and x.endswith('__'), dir(human1.addresses)))))
A legfontosabb metódusokat itt ismertetem:
__reduce__
Amikor egy objektum pácolásra kerül, a __reduce__() metódus végrehajtásával egy olyan objektumot kapunk vissza, amely segít a pácolónak megérteni, hogyan kell azt visszaépíteni.
__reduce_ex__
A __reduce_ex__() metódust a páka előnyben részesíti a __reduce__() metódussal szemben.
A __reduce_ex__() metódus egy egész számú argumentumot vesz fel, amely a protokoll verziója. Visszafelé kompatibilitást biztosít a pácoláshoz, és a pácolt bájtfolyam objektumhoz való felépítésére szolgál.
__reversed__
A __reversed__() metódust akkor hajtjuk végre, amikor egy gyűjteményt megpróbálunk megfordítani a fordított sorrendben.
Ez akkor hajtódik végre, amikor a reversed(collection) vagy collection.reverse() meghívásra kerül. Néha úgy döntünk, hogy megváltoztatjuk a reversed() metódus működését.
A __reversed__() metódus felülírásával elérhetjük a kívánt eredményt.
Szótár
Ezzel elérkeztünk a cikk ötödik szakaszához.
A szótár a Python egyik fő beépített típusa. Ha elvégezzük a dir(human.maps) parancsot, és kiszűrjük a dupla aláhúzással körülvett metódusokat, akkor azzal találkozunk, hogy összesen 29 mágikus metódus van.
print(len(list(filter(lambda x: x.startswith('__') and x.endswith('__'), dir(human1.maps)))))
A 29 mágikus metódus közül itt a 4 legfontosabbat ismertetem:
__delitem__
Ezt a metódust akkor hajtjuk végre, amikor törölünk egy elemet e.g.
del dictionary
__getitem__
Ez a metódus akkor hajtódik végre, amikor egy kulcshoz tartozó elemet próbálunk megszerezni:
item = dictionary
__setitem__
Ezt a metódust akkor hajtjuk végre, amikor megpróbálunk egy elemet beállítani a szótárban:
dictionary = item
__iter__
Ez a metódus egy iterátort ad vissza a gyűjteményhez. Az iterátor segít nekünk a gyűjteményen való iterálásban.
Az __iter__() metódus felülírásával felülbírálhatjuk az iterátor() végrehajtásának módját.
Végül egy megjegyzés az __call__()
Mi lenne, ha hívhatóvá akarnánk tenni az objektumunkat? Tegyük fel, hogy a human objektumot egy hívható human() függvénnyé szeretnénk tenni?
A__call__() metódus lehetővé teszi számunkra, hogy az osztályokat függvényként kezeljük.
human = Human(1, 'Farhad')
human()
Ezt a funkciót úgy tudjuk elérni, hogy a Human osztályunkban megadjuk a __call__() varázsmetódus implementációját.
Ez kiírja:
You attempted to call
Arguments: {}
id=1 (<class ‘int’>). name=Farhad (<class ‘str’>)
Call completed
A __call__() metódus felülírásával most már implementálhatunk egy dekorátort, hogy egy objektumot függvényként adjunk vissza, vagy akár meg is hívhatjuk azokat a könyvtárakat, amelyek függvényeket fogadnak el argumentumként a tényleges objektumok átadásával.
Summary
Ez egy haladó szintű téma Python fejlesztőknek, és mindenkinek ajánlom, aki a Python programozási nyelvet használja/használni szándékozik.
Ez a cikk azt hivatott elmagyarázni, hogy mik azok a mágikus módszerek, és hogyan használhatók Python alkalmazások készítéséhez. Áttekintést adott a leggyakrabban használt mágikus metódusokról egy egyéni osztályban, egészek, karakterláncok, listák és szótár adattípusokról.
Noha a Pythonban minden metódus nyilvános, a kódolási konvenció szerint az összes privát metódust kettős aláhúzással __<method>__()
Ez azt jelenti, hogy a mágikus metódusokat privát metódusoknak szánják. Ez azt is jelenti, hogy az objektum hívója nem hívhatja meg közvetlenül a metódust, és a metódust az az osztály hívja meg belsőleg, amelyik a mágikus metódussal rendelkezik. A mágikus metódusok segítségével nagyobb kontrollt kapunk az alkalmazásunk viselkedése felett.
A mágikus metódusok felülbírálhatók a funkcionalitás gazdagítása és az üzleti igényeknek leginkább megfelelő egyéni logika létrehozása érdekében.
A legfontosabb mágikus metódusok felvázolásának célja, hogy megértsük, szeretnénk-e felülbírálni ezeket a metódusokat az egyéni osztályainkban az alkalmazások gazdagítása érdekében.