import itertools
class Indexable(object):
def __init__(self,it):
self.it = iter(it)
def __iter__(self):
return self.it
def __getitem__(self,index):
try:
return next(itertools.islice(self.it,index,index+1))
except TypeError:
return list(itertools.islice(self.it,index.start,index.stop,index.step))
Si può usare in questo modo:
it = Indexable(fib())
print(it[10])
#144
print(it[2:12:2])
#[610, 1597, 4181, 10946, 28657]
noti che it[2:12:2]
non restituisce [3, 8, 21, 55, 144]
da quando l'iteratore aveva già avanzato 11 elementi a causa della chiamata a it[10]
.
Edit: Se vuoi it[2:12:2]
per tornare [3, 8, 21, 55, 144]
allora forse usare questo invece:
class Indexable(object):
def __init__(self, it):
self.it = iter(it)
self.already_computed = []
def __iter__(self):
for elt in self.it:
self.already_computed.append(elt)
yield elt
def __getitem__(self, index):
try:
max_idx = index.stop
except AttributeError:
max_idx = index
n = max_idx - len(self.already_computed) + 1
if n > 0:
self.already_computed.extend(itertools.islice(self.it, n))
return self.already_computed[index]
Questa versione salva i risultati in self.already_computed
e usa questi risultati se possibile. In caso contrario, calcola più risultati finché non ha un numero sufficiente di per restituire l'elemento o la sezione indicizzata.
Per lo stesso comportamento come una lista, __getitem__ avrebbe bisogno di riavvolgere il generatore. C'è un modo semplice per farlo? –
Non so se ci sia un modo per farlo, facile o no, ma la classe indicizzabile potrebbe semplicemente memorizzare tutti gli elementi già generati in una lista. Quindi '__getitem__' estrarrebbe i numeri direttamente dalla lista, dopo aver fatto avanzare il generatore, se necessario. – MatrixFrog
Grazie MatrixFrog; è quello che ho finito per fare. – unutbu