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))
Se puede utilizar de esta manera:
it = Indexable(fib())
print(it[10])
#144
print(it[2:12:2])
#[610, 1597, 4181, 10946, 28657]
en cuenta que it[2:12:2]
no devuelve [3, 8, 21, 55, 144]
desde el repetidor ya se había avanzado 11 elementos a causa de la llamada a it[10]
.
Editar: Si desea volver a it[2:12:2]
[3, 8, 21, 55, 144]
entonces tal vez utilizar esto en su lugar:
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]
Esta versión guarda los resultados en self.already_computed
y utiliza esos resultados si es posible. De lo contrario, calcula más resultados hasta que tenga suficientes para devolver el elemento indexado o slice.
Para exhibir el mismo comportamiento que una lista, __getitem__ tendría que rebobinar el generador. ¿Hay una manera fácil de hacer eso? –
No sé si hay alguna forma de hacerlo, fácil o no, pero la clase indexable podría almacenar todos los elementos ya generados en una lista. Luego '__getitem__' sacaría los números directamente de la lista, después de avanzar primero el generador si es necesario. – MatrixFrog
Gracias MatrixFrog; eso es lo que terminé haciendo. – unutbu