Python avansat: Ce sunt metodele magice?

Acest articol evidențiază metodele speciale Python pe care orice programator Python trebuie să le cunoască

Farhad Malik

Follow

16 mai, 2020 – 14 min citește

Metodele magice ne ajută să ne îmbogățim aplicațiile. Ele adaugă indirect magie în codul nostru Python. Acesta este un subiect de nivel avansat pentru dezvoltatorii Python și îl recomand tuturor celor care folosesc și/sau intenționează să folosească limbajul de programare Python.

Metodele magice ne oferă mai mult control asupra modului în care se comportă aplicația noastră.

Acest articol își propune să explice ce sunt metodele magice și cum pot fi folosite pentru a construi aplicații Python. Acesta va oferi o prezentare generală a celor mai utilizate metode magice pentru o serie de tipuri de date.

Scopul prezentării metodelor magice cheie este ca noi să înțelegem dacă dorim să suprascriem aceste metode în clasele noastre personalizate pentru a ne îmbogăți aplicațiile.

Metodele magice fac limbajul de programare Python extrem de puternic

Fotografie de Rodion Kutsaev pe Unsplash

Ce sunt metodele magice Python?

Metodele magice Python sunt cunoscute și sub numele de metode speciale sau metode dunder. Ele sunt înconjurate de sublinieri duble, de exemplu __init__().

Un obiect poate avea un număr de metode magice.

Amintiți-vă că totul în Python este un obiect, inclusiv o variabilă/funcție/clasă etc. Obiectele sunt abstractizarea python pentru date.

Metodele magice sunt folosite pentru a construi și inițializa obiecte noi, ne ajută să recuperăm un obiect ca dicționar, sunt folosite pentru a șterge un obiect printre alte operațiuni. Ele sunt folosite atunci când este invocat operatorul +, sau chiar atunci când dorim să reprezentăm un obiect sub forma unui șir de caractere.

Deși fiecare metodă din Python este publică, convenția de codare este de a înconjura toate metodele private prin duble sublinieri __<metoda>__()

Aceasta implică faptul că metodele magice sunt destinate să fie metode private. Înseamnă, de asemenea, că apelantul unui obiect nu trebuie să invoce metoda direct, deoarece metoda este destinată să fie invocată de clasa care are în interior metoda magică.

Potem suprascrie metodele magice pentru a oferi propria noastră funcționalitate personalizată.

Fotografie de Cristian Escobar pe Unsplash

Voi explica conceptele metodelor magice prin crearea unei clase personalizate și apoi voi oferi o prezentare generală a celor mai importante metode magice într-un întreg, șir de caractere, listă și un dicționar.

Pe măsură ce vom avansa în articol, va începe să se construiască o imagine mult mai clară a motivelor pentru care există metodele magice și a modului de utilizare a acestora.

Dacă doriți să înțelegeți limbajul de programare Python de la nivel începător până la un nivel avansat, atunci vă recomand cu căldură articolul de mai jos:

Voi începe subiectul metodelor magice prin crearea unei clase personalizate și apoi voi explica modul în care sunt folosite metodele magice.

Cazul de utilizare pentru a înțelege metodele magice

În fragmentul de mai jos, am creat o clasă numită Human și apoi am instanțiat o instanță a clasei Human.

Acest fragment de cod va fi folosit pentru a ne ajuta să înțelegem metodele magice.

Rețineți, id-ul obiectului human este de tip întreg, atributul name este de tip string, proprietatea addresses este de tip listă și proprietatea maps este de tip dicționar.

Am grupat metodele magice în diferite secțiuni de tip de date pentru a ușura lectura. Cu toate acestea, aceleași metode magice se regăsesc în diferite tipuri de date.

Metodele magice pot fi suprascrise pentru a îmbogăți funcționalitatea și pentru a crea o logică personalizată care se potrivește cel mai bine nevoilor de afaceri.

Clasa:

Să înțelegem metodele magice care sunt asociate cu obiectul uman. Dacă execut metoda dir(human), aceasta va enumera toate funcțiile și numele atributelor obiectului human.

Există în total 29 de metode/atribute care sunt asociate cu obiectul human. Dintre acestea, 26 sunt metode magice.

Este un număr destul de mare de metode speciale. Aceste metode sunt moștenite de la tipul de bază al clasei Human. Prin urmare, ele sunt metodele predefinite pe care le putem folosi/suprapune pentru a îmbogăți clasele.

Partea următoare a secțiunii va explica metodele magice cheie.

__delattr__

Această metodă este apelată atunci când încercăm să ștergem un atribut dintr-o clasă.

Putem suprascrie funcționalitatea prin implementarea metodei în clasa Human:

def __delattr__(self, item):
print('Deleting attribute ' + item)
return object.__delattr__(self, item)

Acum, ori de câte ori încercăm să ștergem un atribut, se va afișa mesajul: Ștergere atribut

Există, de asemenea, metoda __setattr__() pentru a atribui o valoare unui atribut și __getattr__() pentru a obține o valoare din atribut.

Un caz de utilizare a metodei__delattr__() poate fi acela de a împiedica ștergerea anumitor atribute ale unui obiect sau atunci când dorim să efectuăm acțiuni specifice atunci când un anumit atribut este șters.

__dict__

Această metodă returnează un dicționar care reprezintă obiectul. Cheile dicționarului sunt atributele obiectului, iar valorile sunt valorile atributelor.

Ca o instanță:

human = Human(2, 'Malik').__dict__
print(human)

Codul de mai sus returnează:

{‘id’: 2, ‘name’: ‘Malik’, ‘addresses’: , ‘hărți’: {}}

__dir__

Putem suprascrie metoda dir() prin suprascrierea metodei __dir__() din clasă. Ca exemplu, putem elimina metodele interne din rezultatul returnat de metoda dir():

__eq__

Această metodă este apelată atunci când încercăm să efectuăm operația ==. Să considerăm că două obiecte umane sunt egale atunci când atributul lor id este egal chiar dacă numele lor este diferit.

Potem suprascrie metoda __eq__() pentru a obține această funcționalitate:

def __eq__(self, other):
return self.id == other.id

Aceasta va returna acum True:

first = Human(1, 'Farhad')
second = Human(1, 'Malik')
print(first == second)

__format__

De fiecare dată când încercăm să facem string.format(), metoda __format__() este invocată intern.

__ge__

Ca o instanță, să presupunem că există două obiecte Human:

first = Human(1, 'Farhad')
second = Human(2, 'Malik')

Să considerăm, de asemenea, că proiectul nostru are o regulă conform căreia un obiect uman cu un Id mai mare este considerat mai mare decât celălalt obiect uman. Prin urmare, putem suprascrie metoda __gt__() și să implementăm logica personalizată:

def __ge__(self, other):
return self.id >= other.id

Acest lucru va returna acum False, deoarece Id-ul celui de-al doilea obiect uman este mai mare decât cel al primului obiect uman:

print(first >= second)
Returns False

Există, de asemenea, o metodă __lt__() care este executată atunci când se efectuează operatorul ≤.

__hash__

Hashing-ul este utilizat pentru a converti un obiect într-un număr întreg. Hashing-ul se execută atunci când încercăm să setăm un element într-un dicționar/set.

Un algoritm de hashing bun duce la un număr mai mic de coliziuni de hashing. Putem furniza propriul nostru algoritm de hash prin suprascrierea metodei __hash__().

Să considerăm că id-ul obiectului Human trebuie să fie unic în aplicația noastră. Algoritmul __hash__() poate fi suprascris pentru a returna ID-ul self.id ca număr întreg hash:

def __hash__(self):
return self.id

Potem crea două obiecte și le putem salva într-o colecție de seturi. Atunci când interogăm lungimea setului, ne vom aștepta la două elemente în set deoarece ambele obiecte au un id diferit.

first = Human(1, 'Farhad')
second = Human(2, 'Malik')
my_set = set()
print(len(my_set))
#returns 2

Dacă setăm acum id-ul la 1 pentru ambele obiecte și repetăm exercițiul, atunci vom vedea doar un singur element în set deoarece ambele obiecte au aceeași cheie hash deoarece atributul lor id este același, chiar dacă atributul name este diferit.

first = Human(1, 'Farhad')
second = Human(1, 'Malik')
my_set = set()
print(len(my_set))
#returns 1

__init__

Metoda __init__() se execută atunci când dorim să instanțiem o nouă instanță a unei clase prin apelarea constructorului acesteia.

Ca instanță, atunci când am încercat să executăm:

human = Human(1, 'farhad')

Atunci a fost executată metoda __init__().

Potem suprascrie funcționalitatea și putem trece în ea și propriile noastre argumente și comportamente personalizate.

Ca o instanță, metoda __init__() a clasei Human este:

def __init__(self, id, name, addresses=, maps={}):
self.id = id
self.name = name
self.addresses = addresses
self.maps = maps

Fotografie de Cederic X pe Unsplash

__init_subclass__

Aceasta este una dintre cazurile de utilizare a metaclasei. Atunci când o clasă este subclasată și obiectul său este creat, atunci este apelată metoda __init_subclass__().

În esență, metoda informează părintele că a fost subclasată. Acest cârlig poate apoi să inițializeze toate subclasele unei anumite clase. Prin urmare, metoda este utilizată pentru înregistrarea subclaselor și atribuirea de valori implicite atributelor din subclase. Prin urmare, ne permite să personalizăm inițializarea subclaselor.

În articolul următor voi explica cum funcționează metaclasele.

__new__

Când dorim să instanțiem/creăm o nouă instanță a unei clase, se execută metoda __new__(cls).

Ca o instanță, să considerăm că dorim să imprimăm ‘Human creating…’ ori de câte ori este apelat constructorul Human().

Potem suprascrie funcționalitatea metodei __new__(cls) așa cum se arată mai jos:

def __new__(cls, *args, **kwargs):
print('Human creating...')
return object.__new__(cls)

Ca rezultat, Human creating… este tipărit atunci când am încercat să creăm o instanță:

human = Human(1, 'Farhad', , {'London':2, 'UK':3})

__sizeof__

Această metodă este apelată atunci când executăm sys.getsizeof(). Ea returnează dimensiunea obiectului în memorie, în octeți.

__str__

Se tipărește: id=1. name=Farhad.

Funcția __str__() ar trebui să încerce să returneze o reprezentare prietenoasă a obiectului.

__weakref__

Acest obiect __weakref__ returnează lista de referințe slabe la obiectul țintă. În esență, ajută la colectarea gunoiului să informeze referințele slabe că referentul a fost colectat. Prin urmare, previne ca obiectele să acceseze referințele subiacente.

Fotografie de Yousef Espanioly pe Unsplash

Integer

Aceasta ne aduce la următoarea secțiune a articolului. Putem observa că proprietatea id a obiectului human este de tip int. Dacă efectuăm apoi dir() asupra proprietății id și filtrăm metodele care sunt înconjurate de duble liniuțe de subliniere, vom întâlni că există în total 62 de metode magice.

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

Voi explica aici metodele cheie:

__add__

Această metodă este apelată atunci când încercăm să adunăm două numere.

Ca o instanță:

human.id + 2 este același lucru cu human.id.__add__(2)

__și__

Această metodă este executată atunci când încercăm să folosim operatorul & e.g.:

return self & another_value

__bool__

Această metodă se execută atunci când încercăm să efectuăm verificarea booleană asupra unui obiect e.g.

self != 123

__floordiv__

Această metodă se execută atunci când executăm operatorul //.

Fotografie de Johannes Plenio pe Unsplash

__getnewargs__

Ocazional, decapăm obiecte în Python. Decaparea creează o reprezentare în flux de octeți a unui obiect în memorie care poate fi salvată pe disc dacă este necesar.

Metoda __getnewargs__() informează procesul de decapare asupra modului în care trebuie să reîncarce obiectul decapat pentru a recrea obiectul țintă. În special, modul în care obiectul trebuie să fie creat prin transmiterea argumentelor la metoda new(). De aici și denumirea „get new args”.

__index__

În consecință, un obiect poate fi convertit într-un număr întreg prin executarea metodei __index__(). Putem, de asemenea, să suprascriem metoda și să furnizăm propria noastră funcționalitate a modului în care trebuie generat indexul.

__invert__

Această metodă este executată atunci când folosim operatorul ~.

Ca instanță:

first = Human(1, 'Farhad')
print(~first.id)

Este același lucru cu execuția:

first = Human(1, 'Farhad')
print(first.id.__invert__())

__lshift__

Această metodă ne oferă deplasarea la stânga a unei valori, de exemplu valoarea self <<. Putem supraîncărca operatorul << prin suprascrierea metodei __lshift__().

Nota: __rshift__() se execută atunci când efectuăm operatorul >>.

__mod__

Această metodă este apelată atunci când folosim operatorul %.

__neg__

Această metodă este executată atunci când utilizăm operatorul – negativ, de exemplu.

first.id — second.id

__subclasshook__

Această metodă poate fi suprascrisă pentru a personaliza metoda issubclass(). În esență, aceasta returnează True dacă o clasă este o subclasă și False dacă nu este. Metoda returnează, de asemenea, NotImplemented, ceea ce permite utilizarea algoritmului existent.

Metoda poate personaliza rezultatul metodei issubclass().

Fotografie de Patrick Selin pe Unsplash

String

Aceasta ne aduce la următoarea secțiune a articolului.

Vezi că proprietatea name a obiectului human este de tip string. Dacă efectuăm apoi dir(human.name) asupra proprietății și filtrăm metodele care sunt înconjurate de sublinieri duble, vom observa că există în total 33 de metode magice.

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

Voi explica aici cele 4 metode cheie, deoarece restul articolului a evidențiat deja majoritatea metodelor magice.

__contains__

Această metodă este executată atunci când încercăm să verificăm dacă un anumit caracter există.

__len__

Această metodă returnează lungimea șirului de caractere. Este executată atunci când executăm metoda len().

Dacă dorim să numărăm doar anumite caractere pentru a calcula lungimea șirului, atunci metoda __()__ poate fi suprascrisă pentru a oferi această funcționalitate.

__repr__

Această metodă se execută atunci când dorim să creăm o reprezentare prietenoasă pentru dezvoltator a unui obiect.

Rețineți, __repr__ este executată atunci când imprimăm(obiect) dacă nu avem o implementare __str__() în clasa noastră.

def __repr__(self):
return f'id={self.id} ({type(self.id)}). name={self.name} ({type(self.name)})'print(Human(1, 'Farhad'))

Aceasta va imprima:

id=1 (<clasa ‘int’>). name=Farhad (<clasa ‘str’>)

Metoda __repr__() ar trebui să fie destinată să producă reprezentarea oficială a unui obiect.

__iadd__

Ocazional, folosim un operator de adunare alături de atribuirea e.g.

self.id += 1

Acest lucru este echivalent cu self.id = self.id + 1

Metoda iadd() este executată atunci când efectuăm adunarea cu atribuirea.

În plus, metoda __ipow__() este executată atunci când se efectuează **=.

Metoda magică __iand__() este pentru a efectua un ȘI în sens biunivoc cu atribuire și __ior__() este apelată atunci când încercăm să facem !=, cum ar fi: i != j

List

Aceasta ne aduce la următoarea secțiune a articolului. Proprietatea adrese a obiectului human este de tip listă. Dacă efectuăm apoi dir(human.addresses) asupra proprietății addresses și filtrăm metodele care sunt înconjurate de sublinieri duble, vom întâlni că există în total 35 de metode magice.

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

Voi explica aici metodele cheie:

__reduce__

Când un obiect este decapat, metoda __reduce__() este executată pentru a returna un obiect care ajută decapantul să înțeleagă cum să îl construiască înapoi.

__reduce_ex__

Metoda __reduce_ex__() este preferată de pickle față de metoda __reduce__().

Metoda __reduce_ex__() ia un argument întreg care este versiunea protocolului. Oferă compatibilitate retroactivă pentru decapare și este utilizată pentru a construi fluxul de octeți decapat la obiect.

__reversed__

Metoda __reversed__() este executată atunci când încercăm să inversăm o colecție în secvența inversă.

Este executată atunci când este apelată metoda reversed(collection) sau collection.reverse(). Uneori, decidem să modificăm funcționalitatea metodei reversed().

Prin suprascrierea metodei __reversed__(), putem obține rezultatul dorit.

Dicționar

Aceasta ne aduce la cea de-a cincea secțiune a articolului.

Dicționarul este unul dintre principalele tipuri construite în Python. Dacă efectuăm dir(human.maps) și filtrăm metodele care sunt înconjurate de duble sublinieri, vom întâlni că există în total 29 de metode magice.

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

Printre cele 29 de metode magice, voi explica aici cele 4 metode cheie:

__delitem__

Această metodă este executată atunci când ștergem un element e.g.

del dictionary

__getitem__

Această metodă se execută atunci când încercăm să obținem un element pentru o cheie:

item = dictionary

__setitem__

Această metodă se execută atunci când încercăm să setăm un element în dicționar:

dictionary = item

__iter__

Această metodă returnează un iterator pentru colecție. Un iterator ne ajută să iterăm peste o colecție.

Putem suprascrie modul în care este executat iteratorul() prin suprascrierea metodei __iter__().

În sfârșit, o notă despre __call__()

Ce se întâmplă dacă am vrea să facem obiectul nostru apelabil? Să considerăm că am vrut să transformăm obiectul uman într-o funcție human() apelabilă?

Metoda __call__() ne permite să tratăm clasele ca pe niște funcții.

human = Human(1, 'Farhad')
human()

Potem realiza această funcționalitate prin furnizarea implementării metodei magice __call__() în clasa noastră Human.

Aceasta va imprima:

Ai încercat să apelezi
Argumente: ()
Argumente cuvinte cheie: {}
id=1 (<clasa ‘int’>). name=Farhad (<clasa ‘str’>)
Apel finalizat

Prin suprascrierea metodei __call__(), putem acum să implementăm un decorator pentru a returna un obiect ca o funcție sau chiar să apelăm acele biblioteci care acceptă funcții ca argumente prin trecerea obiectelor reale.

Fotografie de Dollar Gill pe Unsplash

Rezumat

Acesta este un subiect de nivel avansat pentru dezvoltatorii Python și îl recomand tuturor celor care sunt/sau intenționează să folosească limbajul de programare Python.

Acest articol și-a propus să explice ce sunt metodele magice și cum pot fi folosite pentru a construi aplicații Python. El a oferit o prezentare generală a celor mai utilizate metode magice într-o clasă personalizată, întregi, șiruri de caractere, liste și tipuri de date de tip dicționar.

Deși fiecare metodă din Python este publică, convenția de codare este de a înconjura toate metodele private prin sublinieri duble __<metoda>__()

Aceasta implică faptul că metodele magice sunt destinate să fie metode private. Înseamnă, de asemenea, că apelantul unui obiect nu trebuie să invoce direct metoda, iar metoda este invocată intern de clasa care are metoda magică. Metodele magice ne permit să avem mai mult control asupra modului în care se comportă aplicația noastră.

Metodele magice pot fi suprascrise pentru a îmbogăți funcționalitatea și a crea o logică personalizată care să se potrivească cel mai bine nevoilor de afaceri.

Scopul sublinierii metodelor magice cheie este ca noi să înțelegem dacă dorim să suprascriem aceste metode în clasele noastre personalizate pentru a îmbogăți aplicațiile.

.

Lasă un răspuns

Adresa ta de email nu va fi publicată.