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 indiceScrittura dei dati tramite indiceE 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'inizioSe l'indice >= len, allora aggiungiamo l'elemento alla fineNegli 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