6.1 Metodi magici
Il sovraccarico degli operatori in Python permette di definire o modificare il comportamento
degli operatori incorporati (ad esempio, +
, -
, *
, /
) per le classi personalizzate.
Questo si fa tramite metodi speciali, noti anche come
metodi magici.
Ad esempio, nel tuo classe puoi sovraccaricare gli operatori di confronto:
Operatore | Metodo senza trattino basso | Firma del metodo |
---|---|---|
== | eq() |
__eq__(self, other) |
!= | ne() |
__ne__(self, other) |
< | lt() |
__lt__(self, other) |
<= | le() |
__le__(self, other) |
> | gt() |
__gt__(self, other) |
>= | ge() |
__ge__(self, other) |
Supponiamo che tu abbia scritto la tua classe e voglia che gli oggetti della tua classe
vengano confrontati proprio come desideri. Devi solo implementare
nella tua classe il metodo «__eq__»
, e Python lo chiamerà ogni
volta che nel codice vengono confrontati oggetti della tua classe.
Esempio:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
return self.x == other.x and self.y == other.y
# Utilizzo
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(4, 5)
print(v1 == v2) # Stampa: True
print(v1 == v3) # Stampa: False
Ogni volta che confronti due dei tuoi oggetti, Python controlla se esiste una
funzione «__eq__»
implementata. Se c'è, la chiama.
Se invece non c'è, confronterà semplicemente i puntatori agli oggetti.
In realtà, nell'esempio sopra è scritto (bisogna solo aggiungere il controllo sulla presenza del metodo):
# Utilizzo
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(4, 5)
print(v1.__eq__(v2)) # Stampa: True
print(v1.__eq__(v3)) # Stampa: False
6.2 Elenco di tutti gli operatori
In totale sono disponibili 6 gruppi di operatori per il sovraccarico.
Operatori aritmetici:
Operatore | Metodo senza trattino basso | Firma del metodo |
---|---|---|
+ | add |
__add__(self, other) |
- | sub |
__sub__(self, other) |
* | mul |
__mul__(self, other) |
/ | truediv |
__truediv__(self, other) |
// | floordiv |
__floordiv__(self, other) |
% | mod |
__mod__(self, other) |
** | pow |
__pow__(self, other) |
Operatori di confronto:
Operatore | Metodo senza trattino basso | Firma del metodo |
---|---|---|
== | eq |
__eq__(self, other) |
!= | ne |
__ne__(self, other) |
< | lt |
__lt__(self, other) |
<= | le |
__le__(self, other) |
> | gt |
__gt__(self, other) |
>= | ge |
__ge__(self, other) |
Operatori logici:
Operatore | Metodo senza trattino basso | Firma del metodo |
---|---|---|
& | and |
__and__(self, other) |
| | or |
__or__(self, other) |
^ | xor |
__xor__(self, other) |
~ | invert |
__invert__(self) |
Operatori di indicizzazione e slicing:
Operatore | Metodo |
---|---|
obj[key] | __getitem__(self, key) |
obj[key] = value | __setitem__(self, key, value) |
del obj[key] | __delitem__(self, key) |
Operatori unari:
Operatore | Metodo |
---|---|
- | __neg__(self) |
+ | __pos__(self) |
abs() | __abs__(self) |
~ | __invert__(self) |
Operatori di assegnazione:
Operatore | Metodo |
---|---|
+= | __iadd__(self, other) |
-= | __isub__(self, other) |
*= | __imul__(self, other) |
/= | __itruediv__(self, other) |
//= | __ifloordiv__(self, other) |
%= | __imod__(self, other) |
**= | __ipow__(self, other) |
Forse è per questo che Python è così lento – ogni volta prima di eseguire un operatore cerca una funzione equivalente nella classe e in tutte le sue classi parenti. Tuttavia, questo permette di scrivere il codice più compatto al mondo :)
6.3 Operatore di indicizzazione
Che si possano confrontare oggetti o sottrarre insiemi è in un certo senso ovvio. E te ne accorgerai da solo se scriverai una classe che presuppone operazioni logiche o matematiche su di essa.
Voglio discutere con te un esempio interessante – come l'operatore di indicizzazione. Iniziamo subito con un esempio di codice:
class CustomList:
def __init__(self, data):
self.data = data
def __getitem__(self, index):
return self.data[index]
def __setitem__(self, index, value):
self.data[index] = value
def __delitem__(self, index):
del self.data[index]
def __repr__(self):
return repr(self.data)
# Utilizzo
c_list = CustomList([1, 2, 3, 4, 5])
print(c_list[1]) # Stampa: 2
c_list[1] = 10
print(c_list) # Stampa: [1, 10, 3, 4, 5]
del c_list[1]
print(c_list) # Stampa: [1, 3, 4, 5]
Qui vediamo un esempio di tre operazioni:
Lettura dei dati tramite indice
Scrittura dei dati tramite indice
E persino eliminazione dei dati tramite indice.
E in effetti non è affatto necessario che i dati all'interno siano memorizzati sotto forma di lista.
O l'indice non deve necessariamente essere un numero. Ad esempio, la classe
dictionary
(dizionario) è implementata proprio così.
Creare una SuperList
Ricordi la classe list
? Puoi assegnare elementi, ma solo con quegli
indici che già esistono. Creiamo una nostra classe, chiamiamola
SuperList
, ai cui elementi potrai accedere da qualsiasi indice:
Se l'indice < 0, allora inseriamo l'elemento all'inizio
Se l'indice >= len, allora aggiungiamo l'elemento alla fine
Negli altri casi restituiamo semplicemente l'elemento
Esempio:
class SuperList(list):
def __init__(self, value):
super().__init__(value)
def __setitem__(self, index, value):
if index >= len(self):
super().append(value)
elif index < 0:
super().insert(0, value)
else:
super().__setitem__(index, value)
lst = SuperList([1, 2, 3])
lst[200] = 100
lst[-200] = 99
print(lst) # [99, 1, 2, 3, 100]
Quindi il sovraccarico degli indici è una grande possibilità, e ti consiglio di usarlo in pratica. E per oggi è tutto.
GO TO FULL VERSION