2013-05-19 19 views
7

Dokumentacja itertools.cycle Pythona() daje wdrażanie pseudo-kod jako:Dlaczego itertools.crypt Pythona musi utworzyć kopię pliku iterable?

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 

poniżej, stwierdza: „Uwaga, to członek Toolkit może wymagać znaczącego pamięci dyskowej (w zależności od długości iterable). "

I w zasadzie szło tą drogą, z wyjątkiem Zrobiłem to, co nie wymaga tworzenia kopię iterable:

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 

drukuje:

1 
2 
3 
1 
2 
3 

Tak, zdaję sobie sprawę, moje wykonanie nie działałby dla strs, ale można by to zrobić. Jestem bardziej ciekawy, dlaczego tworzy kolejną kopię. Mam wrażenie, że ma to związek ze zbieraniem śmieci, ale nie jestem dobrze zbadany w tym obszarze Pythona.

Dzięki!

+0

@Martijn jak dodałeś podświetlanie składni? Nie mogłem wymyślić, jak to zrobić ... – stantonk

+0

Nie zrobiłem tego; Dodałem tag 'python', a następnie podświetlanie zostanie zastosowane automatycznie. Ale nawet bez podświetlania znacznika jest często dokładnie zgadywana. –

+0

Ahhh, ale tag python2.7 nie? dziwne ... – stantonk

Odpowiedz

11

Iterables można powtarzać tylko po po.

Tworzysz nowy iterowalny w pętli. Cycle nie może tego zrobić, musi działać z tym, co przekazałeś. cycle nie może po prostu odtworzyć iteracji. Jest więc zmuszony do przechowywania wszystkich elementów, które produkuje oryginalny iterator.

Jeśli było przejść w poniższej generatora Zamiast tego, loop() zawiedzie:

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

teraz Twój loop() produkuje:

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

Twój kod będzie działać tylko dla sekwencji, dla których za pomocą cycle() byłoby przesadą; w tym przypadku nie potrzebujesz obciążenia pamięci masowej cycle(). Uprość to do:

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

Nie jestem pewien, co tu odpowiadasz ... moje pytanie brzmi: dlaczego trzeba utworzyć kopię danych wejściowych? Zwracają uwagę na konsekwencje magazynowania pomocniczego ... dlaczego nie rozwiązać tego problemu? – stantonk

+0

@stantonk: o to właśnie mówię; Pokazuję ci, jak iteratory mogą tworzyć tylko te elementy * raz *. Musisz zachować te elementy, jeśli chcesz je nadal powtarzać. –

+0

@stantonk: Zostałeś oszukany przez zdolność 'set' do tworzenia nowych iteratorów; ma nadal elementy, więc za każdym razem może utworzyć nowy iterator dla tych samych elementów. –

Powiązane problemy