2013-05-19 18 views
7

La documentazione per itertools.cycle Python() fornisce un'implementazione pseudo-codice come:Perchè itertools.cycle di Python deve creare una copia del iterabile?

def cycle(iterable): 
    # cycle('ABCD') --> A B C D A B C D A B C D ... 
    saved = [] 
    for element in iterable: 
     yield element 
     saved.append(element) 
    while saved: 
     for element in saved: 
       yield element 

seguito, si afferma: "Nota, questo membro del toolkit può richiedere notevole spazio di memoria (a seconda della lunghezza del l'iterabile). "

io in fondo stavo andando su questa strada, tranne che ho fatto questo, che non richiede la creazione di una copia del iterabile:

def loop(iterable): 
    it = iterable.__iter__() 

    while True: 
     try: 
      yield it.next() 
     except StopIteration: 
      it = iterable.__iter__() 
      yield it.next() 

x = {1, 2, 3} 

hard_limit = 6 
for i in loop(x): 
    if hard_limit <= 0: 
     break 

    print i 
    hard_limit -= 1 

stampe:

1 
2 
3 
1 
2 
3 

Sì, mi rendo conto che la mia implementazione non funzionerebbe per gli str, ma potrebbe essere fatto a. Sono più curioso del motivo per cui crea un'altra copia. Ho la sensazione che abbia a che fare con la garbage collection, ma non sono ben studiato in quest'area di Python.

Grazie!

+0

@Martijn come è stata aggiunta l'evidenziazione della sintassi? Non riuscivo a capire come farlo ... – stantonk

+0

Non l'ho fatto; Ho aggiunto il tag 'python' e quindi l'evidenziazione viene applicata automaticamente. Ma anche senza l'evidenziazione del tag è spesso indovinato in ogni caso. –

+0

Ahhh, ma il tag python2.7 non lo fa? strano ... – stantonk

risposta

11

Iterables può essere iterato solo su una volta.

Si crea un nuovo iterabile nel proprio ciclo. Il ciclo non può farlo, deve funzionare con qualsiasi cosa tu abbia passato. cycle non può semplicemente ricreare l'iterabile. È quindi costretto a memorizzare tutti gli elementi che produce l'iteratore originale.

Se si dovesse passare in seguito generatore, invece, il vostro loop() fallisce:

def finite_generator(source=[3, 2, 1]): 
    while source: 
     yield source.pop() 

Ora il vostro loop() produce:

>>> hard_limit = 6 
>>> for i in loop(finite_generator()): 
...  if hard_limit <= 0: 
...   break 
...  print i 
...  hard_limit -= 1 
... 
1 
2 
3 

Il tuo codice dovrebbe funzionare solo per le sequenze, per i quali utilizzando cycle() sarebbe eccessivo; in tal caso non è necessario il carico di archiviazione di cycle(). Semplificarlo a:

def loop_sequence(seq): 
    while True: 
     for elem in seq: 
      yield elem 
+0

Non sono proprio sicuro di cosa stai rispondendo qui ... la mia domanda è: perché è necessario creare una copia dell'input? Fanno un punto per notare le implicazioni dello storage ausiliario ... perché non risolvere questo problema? – stantonk

+0

@stantonk: questo è quello che sto affrontando; Ti mostro come gli iteratori possono produrre i loro elementi * una volta *. È necessario memorizzare quegli elementi se si desidera continuare a ripeterli. –

+0

@stantonk: Sei stato ingannato dall'abilità 'set's di creare nuovi iteratori; ha ancora gli elementi così può produrre ogni volta un nuovo iteratore per gli stessi elementi. –

Problemi correlati