Ce sunt metodele magice în Python și cum să le folosești

O caracteristică mai puțin explorată, dar extrem de utilă a limbajului Python, constă în posibilitatea de a integra metode magice în cadrul obiectelor. Prin intermediul acestor metode, codul devine mai clar, intuitiv și ușor de urmărit.

Utilizând metodele magice, se pot crea interfețe de interacțiune cu obiectele, care să respecte spiritul Python. Acest articol își propune să prezinte conceptul metodelor magice, să discute cele mai eficiente modalități de implementare și să analizeze metodele magice comune pe care le veți întâlni.

Ce sunt metodele magice?

Metodele magice sunt funcții speciale în Python, care definesc modul în care obiectele se comportă în momentul aplicării operațiilor uzuale. Aceste metode sunt identificate prin prezența a două caractere underscore (liniuță de subliniere) atât înainte, cât și după denumirea metodei.

Astfel, sunt adesea denumite „metode dunder”, derivat din „double underscore”. Un exemplu comun este metoda __init__(), folosită pentru a defini constructorii claselor.

De obicei, metodele dunder nu sunt apelate direct în codul sursă; interpretorul Python le va invoca automat în timpul execuției programului.

De ce sunt utile metodele magice?

Metodele magice sunt un concept valoros în programarea orientată pe obiecte în Python. Ele permit specificarea comportamentului tipurilor de date personalizate, în contextul operațiilor încorporate comune. Aceste operații includ:

🟢 Operații aritmetice

🟢 Operații de comparare

🟢 Operații legate de ciclul de viață al obiectelor

🟢 Operații de reprezentare

Următoarea secțiune va explica cum se implementează metodele magice, care determină comportamentul aplicației în raport cu categoriile enumerate mai sus.

Cum se definesc metodele magice

După cum am precizat anterior, metodele magice reglementează comportamentul obiectelor. Din acest motiv, acestea se definesc ca parte a clasei obiectului. Fiind incluse în clasă, au ca prim argument „self”, care este o referință către obiectul în sine.

În funcție de modul în care sunt apelate de interpretor, pot primi și argumente suplimentare. De asemenea, sunt identificate clar prin cele două caractere underscore înainte și după nume.

Implementarea

Până acum, discuția a avut un caracter mai degrabă teoretic și abstract. În această secțiune, vom trece la implementarea unei clase simple, Rectangle.

Această clasă va avea atribute de lungime și lățime. Prin metoda __init__, vom putea defini aceste atribute la instanțiere. Mai mult, vom putea compara diferite dreptunghiuri pentru a stabili dacă sunt egale, mai mici sau mai mari, utilizând operatorii ==, < și >. În final, dreptunghiul va oferi o reprezentare string relevantă.

Configurarea mediului de codare

Pentru a putea urmări explicația, veți avea nevoie de un mediu de execuție Python. Puteți folosi unul local sau un compilator Python online, cum ar fi tipstrick.ro.

Crearea clasei dreptunghi

Începem prin a defini clasa Rectangle.

class Rectangle:
    pass

Crearea metodei constructor

Următorul pas este să creăm prima noastră metodă magică, constructorul clasei. Această metodă va accepta înălțimea și lățimea și le va stoca ca atribute ale instanței clasei.

class Rectangle:
    def __init__(self, height, width):
        self.height = height
        self.width = width

Crearea unei metode magice pentru reprezentarea string

În continuare, vom crea o metodă care permite clasei să genereze un șir de caractere, ușor de citit, reprezentând obiectul. Această metodă va fi invocată ori de câte ori apelăm funcția str() cu o instanță a clasei Rectangle ca argument. De asemenea, va fi apelată când folosim funcții care așteaptă un argument de tip string, cum ar fi funcția print().

class Rectangle:
    def __init__(self, height, width):
        self.height = height
        self.width = width

    def __str__(self):
        return f'Rectangle({self.height}, {self.width})'

Metoda __str__() trebuie să returneze șirul prin care dorim să reprezentăm obiectul. În acest caz, returnăm un șir de formatul Rectangle(, ), unde înălțimea și lățimea reprezintă dimensiunile stocate ale dreptunghiului.

Crearea de metode magice pentru operațiile de comparare

Acum vom crea operatori de comparație pentru operațiile de egalitate, mai mic și mai mare. Acest proces se numește supraîncărcarea operatorului. Pentru a le realiza, folosim metodele magice __eq__, __lt__ și, respectiv, __gt__. Aceste metode vor returna o valoare booleană după compararea ariilor dreptunghiurilor.

class Rectangle:
    def __init__(self, height, width):
        self.height = height
        self.width = width

    def __str__(self):
        return f'Rectangle({self.height}, {self.width})'

    def __eq__(self, other):
        """ Verifică egalitatea """
        return self.height * self.width == other.height * other.width

    def __lt__(self, other):
        """ Verifică dacă un dreptunghi este mai mic decât altul """
        return self.height * self.width < other.height * other.width

    def __gt__(self, other):
       """ Verifică dacă un dreptunghi este mai mare decât altul """
        return self.height * self.width > other.height * other.width

După cum se observă, aceste metode acceptă doi parametri. Primul este dreptunghiul curent, iar al doilea este valoarea cu care se face comparația. Această valoare poate fi o altă instanță de dreptunghi sau orice altă valoare. Logica comparației și condițiile care determină rezultatul (adevărat sau fals) depind în totalitate de implementare.

Metode magice comune

În continuare, vom analiza metodele magice comune pe care le veți utiliza frecvent.

#1. Operații aritmetice

Metodele magice aritmetice sunt invocate atunci când o instanță a clasei este poziționată în partea stângă a unui semn aritmetic. Metoda va fi apelată cu două argumente: primul fiind o referință la instanță, iar al doilea, obiectul din partea dreaptă a semnului. Metodele și semnele sunt următoarele:

Nume Metodă Semn Descriere
Adunare __add__ + Implementează adunarea
Scădere __sub__ Implementează scăderea
Înmulțire __mul__ * Implementează înmulțirea
Împărțire __div__ / Implementează împărțirea
Împărțire cu rest __floordiv__ // Implementează împărțirea cu rest

#2. Operații de comparare

Similar metodelor magice aritmetice, aceste metode sunt invocate când o instanță a clasei pentru care sunt definite este plasată în partea stângă a operatorului de comparație. Ca și în cazul metodelor aritmetice, ele primesc doi parametri: primul este o referință la instanța obiectului, iar al doilea este o referință către valoarea din dreapta semnului.

Nume Metodă Semn Descriere
Mai mic decât __lt__ < Implementează comparația „mai mic decât”
Mai mare decât __gt__ > Implementează comparația „mai mare decât”
Egal cu __eq__ == Implementează comparația „egal cu”
Mai mic sau egal cu __le__ <= Implementează comparația „mai mic sau egal cu”
Mai mare sau egal cu __ge__ >= Implementează comparația „mai mare sau egal cu”

#3. Operațiuni ale ciclului de viață

Aceste metode sunt apelate ca răspuns la diferitele etape ale ciclului de viață al unui obiect, cum ar fi instanțierea sau ștergerea. Constructorul, __init__, se încadrează în această categorie. Metodele uzuale din această categorie sunt listate în tabelul de mai jos:

Nume Metodă Descriere
Constructor __init__ Această metodă este apelată de fiecare dată când se creează un obiect din clasa pentru care este definită.
Ștergere __del__ Această metodă este invocată când un obiect al clasei respective este șters. Poate fi utilizată pentru a efectua acțiuni de curățare, cum ar fi închiderea fișierelor deschise.
Nou __new__ Metoda __new__ este apelată prima dată când se instanțiază un obiect al clasei. Această metodă este executată înaintea constructorului și primește clasa și orice argumente suplimentare. Returnează o instanță a clasei. Deși, în general, nu este extrem de utilă, este abordată în detaliu aici.

#4. Operațiuni de reprezentare

Nume Metodă Descriere
String __str__ Returnează o reprezentare string, ușor de citit de către om, a obiectului. Această metodă este invocată atunci când apelăm funcția str(), cu o instanță a clasei ca argument. De asemenea, este apelată când trecem instanța ca argument funcțiilor print() și format(). Scopul ei este de a oferi un șir inteligibil utilizatorului final al aplicației.
Reprezentare __repr__ Returnează o reprezentare string a obiectului, destinată programatorului. Ideal, șirul returnat ar trebui să fie bogat în informații, astfel încât să poată fi construită o instanță identică a obiectului, doar din acest șir.

Cele mai bune practici pentru crearea de metode magice

Metodele magice sunt extrem de utile și pot simplifica codul. Totuși, este important să țineți cont de următoarele aspecte atunci când le utilizați:

  • Folosiți-le cu moderație – Implementarea unui număr excesiv de metode magice poate face codul dificil de înțeles. Limitați-vă la cele esențiale.
  • Asigurați-vă că înțelegeți implicațiile de performanță ale anumitor metode, precum __setattr__ și __getattr__, înainte de a le utiliza.
  • Documentați comportamentul metodelor magice, astfel încât alți dezvoltatori să știe exact ce fac. Astfel, devin mai ușor de utilizat și de depanat.

Concluzie

În acest articol, am prezentat metodele magice ca o modalitate de a crea clase care pot fi utilizate cu operații încorporate. Am explicat cum se definesc și am analizat un exemplu de clasă unde metodele magice au fost implementate. Apoi, am enumerat metodele pe care probabil le veți utiliza și de care veți avea nevoie, înainte de a trece în revistă câteva bune practici de reținut.

Ca pas următor, ați putea dori să învățați cum se implementează clasa Counter în Python.