2016-04-27 6 views
-1

Ich beobachtete dieses Video (http://pyvideo.org/video/1758/loop-like-a-native-while-for-iterators-genera) wo am Ende er spricht darüber, wie der Generator Ausdruck ist das gleiche wie der normale Generator Weg, aber das scheint nicht der Fall zu sein und andere Themen, die ich hab gelesen, dass generator expression vs yield sagt es gibt keinen unterschied. Was ich jedoch unter Verwendung der Ausbeute sehen kann, wird jedes Mal an die for-Schleife zurückgegeben, wenn der Ausdruck des Generators dies nicht tut. Es schließt seine Aufgabe ab und kehrt dann zur for-Schleife zurück. Das könnte ein ziemlich großer Unterschied in der Speichernutzung sein (abhängig davon, was Sie gerade durchlaufen), oder? Habe ich recht in meinem Denken?Generator Ausdrücke vs Ausbeute

# generators call yield which will return to the loop it's called in before coming back 
def evens(stream): 
    for n in stream: 
     if n % 2 == 0: 
      print("Inside evens") 
      yield n 

# this is the same as above just a generator expression 
def evens2(stream): 
    print("Inside evens2") 
    return (n for n in stream if n % 2 == 0) 
+1

Ihre 'print()' Anweisung ist an der falschen Stelle in 'evens'. Es sollte am Anfang sein, nicht in der Schleife. –

+0

Ihr Generatorausdruck und Ihre Generatorfunktion verhalten sich in * genau gleich * sonst. Beide ergeben "n" nur beim Iterieren. –

Antwort

4

Sie liegen falsch in Ihrem Denken. Ihr Generator Ausdruck tut genau die gleiche Sache wie die Generator-Funktion, mit nur einem Unterschied: Sie haben den print() Anruf an der falschen Stelle platziert. In evens2 drucken Sie vor der Generator Ausdruck wurde ausgeführt, Erstellen eines Generator-Objekts, während in evens Sie innerhalb der Generator-Funktion selbst drucken.

Wenn dies Python 3 (oder Sie haben from __future__ import print_function) können Sie die print() Funktion im Inneren des Generators Ausdruck verwenden:

def evens2(stream): 
    return (print('inside evens2') or n for n in stream if n % 2 == 0) 

Dies ist das Äquivalent von:

def evens(stream): 
    for n in stream: 
     if n % 2 == 0: 
      yield print("Inside evens") or n 

print() immer zurückkehrt None, so wird print(..) or nn zurückgeben. Iteration über beide wird sowohl drucken als auch alle n Werte ergeben.

Demo:

>>> def evens2(stream): 
...  return (print('inside evens2') or n for n in stream if n % 2 == 0) 
... 
>>> def evens(stream): 
...  for n in stream: 
...   if n % 2 == 0: 
...    yield print("Inside evens") or n 
... 
>>> g1 = evens([1, 2, 3, 4, 5]) 
>>> g2 = evens2([1, 2, 3, 4, 5]) 
>>> g1 
<generator object evens at 0x10bbf5938> 
>>> g2 
<generator object evens2.<locals>.<genexpr> at 0x10bbf5570> 
>>> next(g1) 
Inside evens 
2 
>>> next(g2) 
inside evens2 
2 
>>> next(g1) 
Inside evens 
4 
>>> next(g2) 
inside evens2 
4 

Beiden Anrufe erzeugen ein Generator-Objekt, und beide Generator-Objekte drucken zusätzliche Informationen jedes Mal, wenn sie einen Schritt mit next() vorantreiben.

Soweit Python betrifft, erzeugen die beiden Generatoren Gegenstände mehr oder weniger die gleichen Bytecode:

>>> import dis 
>>> dis.dis(compile('(n for n in stream if n % 2 == 0)', '', 'exec').co_consts[0]) 
    1   0 LOAD_FAST    0 (.0) 
     >> 3 FOR_ITER    27 (to 33) 
       6 STORE_FAST    1 (n) 
       9 LOAD_FAST    1 (n) 
      12 LOAD_CONST    0 (2) 
      15 BINARY_MODULO 
      16 LOAD_CONST    1 (0) 
      19 COMPARE_OP    2 (==) 
      22 POP_JUMP_IF_FALSE  3 
      25 LOAD_FAST    1 (n) 
      28 YIELD_VALUE 
      29 POP_TOP 
      30 JUMP_ABSOLUTE   3 
     >> 33 LOAD_CONST    2 (None) 
      36 RETURN_VALUE 
>>> dis.dis(compile('''\ 
... def evens(stream): 
...  for n in stream: 
...   if n % 2 == 0: 
...    yield n 
... ''', '', 'exec').co_consts[0]) 
    2   0 SETUP_LOOP    35 (to 38) 
       3 LOAD_FAST    0 (stream) 
       6 GET_ITER 
     >> 7 FOR_ITER    27 (to 37) 
      10 STORE_FAST    1 (n) 

    3   13 LOAD_FAST    1 (n) 
      16 LOAD_CONST    1 (2) 
      19 BINARY_MODULO 
      20 LOAD_CONST    2 (0) 
      23 COMPARE_OP    2 (==) 
      26 POP_JUMP_IF_FALSE  7 

    4   29 LOAD_FAST    1 (n) 
      32 YIELD_VALUE 
      33 POP_TOP 
      34 JUMP_ABSOLUTE   7 
     >> 37 POP_BLOCK 
     >> 38 LOAD_CONST    0 (None) 
      41 RETURN_VALUE 

Beide FOR_ITER Schleifen verwenden, COMPARE_OP zu sehen, ob die Ausgabe von BINARY_MODULO gleich 0 ist und beide verwenden YIELD_VALUE, um den Wert n zu ergeben.

+0

ty für die Erklärung. Ich war mir nicht sicher, ob ich dort eine gedruckte Erklärung abgeben könnte. Also Generatorausdrücke verbergen nur die yield-Anweisung von uns. – user441521

+0

@ user441521: genau, genau wie ein Listenverständnis verbirgt den 'list.append()' Anruf von uns. –