Powiedz, że mam generator dla sets:

def f(n) :
  for i in xrange(n) :
    yield set(xrange(i) )

>>> for s in f(5) :
      print s

set([])
set([0])
set([0, 1])
set([0, 1, 2])
set([0, 1, 2, 3])

Teraz chcę ich union. Mogę utworzyć tymczasową listę zestawów i rozpakować tę listę w argumenty dla union:

>>> set.union( * list( f(5) ) )
set([0, 1, 2, 3])

Mogę również dać generatorowi do union:

>>> set.union( * f(5) )
set([0, 1, 2, 3])

Czy drugie podejście tworzy pełną tymczasową listę jak pierwszy? Które podejście jest skuteczne?

3
usual me 17 sierpień 2014, 06:00

2 odpowiedzi

Najlepsza odpowiedź

Python rozszerza generator pierwszy przy stosowaniu go jako argumentów; Wszystkie wartości wytwarzane przez generator są ładowane do pamięci przed rozpoczęciem połączenia w obu opcjach.

Możesz użyć a reduce() Funkcja Zadzwoń:

from functools import reduce  # Python 3 forward compatibility

reduce(set.union, f(5))

To iteruje na wartościach wytworzonych przez f(5) jeden po drugim bez budowania ich najpierw sekwencji.

Próbny:

>>> def f(n):
...     for i in xrange(n):
...         yield set(xrange(i))
... 
>>> reduce(set.union, f(5))
set([0, 1, 2, 3])
8
Martijn Pieters 5 lipiec 2017, 12:53

Oba podejścia oceni (i przechowują) wszystkie elementy generatora. Podczas wywoływania funkcji cała lista argumentów musi zostać oceniona przed wywołaniem funkcji.

Możesz to zobaczyć z małym przykładem:

def f(n):
    for i in xrange(n) :
        yield set(xrange(i) )
    1/0

def blah(*args):
    print "Blah!"

>>> blah(*f(5))
Traceback (most recent call last):
  File "<pyshell#56>", line 1, in <module>
    blah(*f(5))
  File "<pyshell#52>", line 4, in f
    1/0
ZeroDivisionError: division by zero

Zauważ, że "bla!" nie jest drukowany. Ponieważ wyjątek jest podniesiony podczas próby oceny wszystkich elementów w generator f(5), wezwanie do {x1}} nigdy nie ma miejsca.

4
BrenBarn 17 sierpień 2014, 02:01