Advanced Python: Python: Mik azok a mágikus módszerek?

Ez a cikk kiemeli azokat a Python speciális módszereket, amelyeket minden Python programozónak ismernie kell

Farhad Malik

Follow

május 16, 2020 – 14 min olvasni

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

Photo by Rodion Kutsaev on Unsplash

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.

Photo by Cristian Escobar on Unsplash

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

Photo by Cederic X on Unsplash

__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.

Photo by Yousef Espanioly on Unsplash

Integer

Ezzel elérkeztünk a cikk következő részéhez. Láthatjuk, hogy a human objektum id tulajdonsága int típusú. Ha ezután dir() műveletet végzünk az id tulajdonságon, és kiszűrjük azokat a metódusokat, amelyeket kettős aláhúzás vesz körül, akkor azzal találkozunk, hogy összesen 62 mágikus metódus van.

print(len(list(filter(lambda x: x.startswith('__') and x.endswith('__'), dir(human1.id)))))

A legfontosabb metódusokat itt ismertetem:

__add__

Ezt a metódust akkor hívjuk meg, amikor két számot próbálunk összeadni.

Egy példa:

human.id + 2 ugyanaz, mint human.id.__add__(2)

__and__

Ezt a metódust akkor hajtjuk végre, amikor az & e operátort próbáljuk használni.g.:

return self & another_value

__bool__

Ez a metódus akkor hajtódik végre, amikor egy objektumon boolean ellenőrzést próbálunk végrehajtani pl.

self != 123

__floordiv__

Ez a metódus akkor hajtódik végre, amikor a //operátort végrehajtjuk.

Photo by Johannes Plenio on Unsplash

__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.

Photo by Patrick Selin on Unsplash

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.

Photo by Dollar Gill on Unsplash

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.

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.