In Python, il protocollo 'Sequence' definisce un'interfaccia per oggetti che possono essere indicizzati e iterati, come ad esempio liste o tuple. Storicamente, le sequenze sono emerse praticamente dalle prime versioni di Python per supportare operazioni naturali con le strutture dati: indicizzazione, slicing, iterazione degli elementi.
Problema — affinché una classe utente si comporti come una sequenza, non bastano i metodi iter e next. Per supportare completamente il comportamento delle sequenze sono necessari metodi aggiuntivi.
Soluzione — per implementare il proprio tipo di sequenza, è necessario definire il metodo getitem (necessario per l'indicizzazione e gli slicing) e opzionalmente len (per len() e controllo della lunghezza). In questo modo, l'oggetto supporterà l'iterazione, l'accesso per indice, il lavoro con gli slicing, oltre a molte operazioni standard di Python con le sequenze.
Esempio di codice:
class MyCounter: def __init__(self, stop): self._stop = stop def __getitem__(self, index): if 0 <= index < self._stop: return index * 10 else: raise IndexError('Fuori intervallo') def __len__(self): return self._stop c = MyCounter(5) print(c[3]) # 30 print(len(c)) # 5 for x in c: print(x)
Caratteristiche chiave:
Se implemento solo iter e next, il mio oggetto diventa una sequenza (Sequence)?
No. Tale oggetto sarà solo iterabile (iterable), ma non una sequenza. Non supporterà l'indicizzazione, gli slicing, le funzioni standard degli oggetti simili a liste.
È necessario implementare getitem per supportare il ciclo for?
Non è necessario. Se iter è implementato, il for funzionerà. Ma se non c'è iter, l'interprete proverà a utilizzare getitem, iniziando dall'indice 0, fino a quando non si verificherà un IndexError. Pertanto, per le sequenze è sufficiente getitem.
Posso implementare getitem solo per int, e non per slice?
Tecnicamente, l'oggetto funzionerà per c[0], ma il tentativo di fare uno slicing c[1:4] fallirà. Per supportare gli slicing, getitem deve essere in grado di gestire oggetti di tipo slice (vedere slice.indices e isinstance(key, slice)).
Esempio di codice:
class S: def __getitem__(self, idx): if isinstance(idx, slice): return [x for x in range(idx.start or 0, idx.stop or 10, idx.step or 1)] return idx * 2
Hanno implementato una struttura personalizzata definendo solo iter, pensando che ora potessero usare slicing e indicizzazione.
Pro:
Contro:
La classe implementa getitem con supporto per lo slicing e gli indici int, nonché len.
Pro:
Contro: