
Il metodo __getitem__() in Python
Il metodo `__getitem__()` in Python un metodo speciale, anche noto come "magic method", che gestisce sia l'indicizzazione che lo slicing.
obj.__getitem__(index)
Questo metodo viene chiamato automaticamente ogni volta che tenta di accedere a un elemento di un oggetto usando le parentesi quadre [ ].
L'operatore [ ] in Python viene utilizzato per accedere agli elementi di una collezione, come liste, tuple o dizionari, e invoca il metodo speciale __getitem__() dell'oggetto su cui viene applicato.
Ad esempio, definisci una lista
my_list = ["a","b","c","d","e"]
Poi accedi alla lista indicando tra le parentesi quadre la posizione dell'elemento che vuoi leggere.
print(my_list[0])
a
Quando scrivi `my_list[0]` Python sta in realtà chiamando il metodo `__getitem__()` sulla lista `my_list`, passando `0` come argomento.
Ricorda che in Python l'indice della posizione degli elementi comincia da zero. Quindi, il primo elemento si trova alla posizione zero mentre il secondo elemento alla posizione uno e via dicendo.
Puoi ottenere lo stesso risultato chiamando direttamente il metodo `__getitem__()` sulla lista.
Anche in questo caso tra le parentesi tonde devi indicare come argomento la posizione dell'elemento che vuoi leggere.
print(my_list.__getitem__(0))
a
Per accedere al secondo elemento della lista, invece, devi indicare la posizione 1.
print(my_list.__getitem__(1))
b
Il metodo __getitem__() viene chiamato anche nel caso degli oggetti iterabili.
Ad esempio, trasforma la lista in un oggetto iterabile
obj=my_list.__iter__()
Poi usa il metodo next per accedere dal primo all'ultimo elemento dell'oggetto
print(next(obj))
Il metodo next chiama automaticamente il metodo __getitem__(0) e restituisce il primo elemento della lista my_list.
a
Ora usa nuovamente il metodo next.
print(next(obj))
Il metodo next chiama automaticamente il metodo __getitem__(1) e restituisce il secondo elemento della lista my_list.
b
E via dicendo
Definire una classe che usa il metodo `__getitem__()`
Il metodo __getitem__() ti permette anche di definire una classe e un oggetto che si comporti come una lista, un dizionario o qualsiasi altro tipo di oggetto iterabile.
In questo modo puoi creare nuove strutture dati personalizzate tramite l'overloading dell'operatore `[ ]`.
Cos'è l'overloading? L'overloading ti consente di modificare il comportamento dei metodi di un oggetto all'interno di una classe. Ad esempio, invece di leggere e restituire un elemento, puoi aggiungere delle operazioni o dei calcoli sull'elemento prima che venga restituito.
Un esempio pratico
Immagina di voler creare una classe che rappresenta una semplice lista di numeri, ma con un comportamento leggermente diverso.
Supponi di voler calcolare il quadrato del numero ogni volta che accedi a un elemento.
class MiaLista:
def __init__(self, numeri):
self.numeri = numeri
def __getitem__(self, indice):
# Restituiamo il quadrato dell'elemento all'indice specificato
return self.numeri[indice] ** 2
Nella classe MiaLista il metodo `__getitem__()` è stato sovrascritto per restituire il quadrato del numero all'indice specificato.
Quando accedi a un elemento, Python chiama `__getitem__(0)` e ti restituisce il suo quadrato.
Ad esempio, crea un'istanza di MiaLista
lista = MiaLista([1, 2, 3, 4, 5])
Ora accedi agli elementi come se fosse una lista normale
print(lista[1])
Il risultato è 4 perché il secondo elemento della lista è 2 e il quadrato è 22 = 4
4
Esempio 2
Ora facciamo un esempio un po' più complesso.
Immagina di voler creare un dizionario case-insensitive che ignora il maiuscolo/minuscolo nelle chiavi:
class CaseInsensitiveDict:
def __init__(self, dati):
self.dati = {chiave.lower(): valore for chiave, valore in dati.items()}
def __getitem__(self, chiave):
# Converte la chiave in minuscolo prima di cercarla nel dizionario
return self.dati[chiave.lower()]
In questa classe il metodo `__getitem__()` converte sempre la chiave in minuscolo prima di cercarla nel dizionario interno.
Ad esempio, crea un'istanza di CaseInsensitiveDict
dizionario = CaseInsensitiveDict({'Nome': 'Alice', 'Anni': 30})
Ora puoi accedere alle chiavi ignorando il maiuscolo/minuscolo
Ad esempio, i comandi `dizionario['Nome']` e `dizionario['nome']` restituiscono entrambi lo stesso valore.
print(dizionario['nome'])
'Alice'
Puoi accedere allo stesso valore anche se digiti tutta la chiave in maiuscolo.
print(dizionario['NOME'])
'Alice'
Il risultato finale è sempre lo stesso.
In questo modo hai creato un dizionario che non fa distinzione tra maiuscole e minuscole nelle chiavi (case insensitive).
Nota. Come puoi notare in questo esempio, l'indice di un oggetto non è necessariamente sempre un numero intero. Nel caso dei dizionari l'indice è in realtà la chiave.
È un esempio pratico di come questo metodo ti consenta di personalizzare l'accesso agli elementi di una collezione all'interno della tua classe, permettendoti così di creare comportamenti complessi e specifici per i tuoi oggetti.