2016-04-27 2 views
-1

Я смотрел это видео (http://pyvideo.org/video/1758/loop-like-a-native-while-for-iterators-genera), где в конце он говорит о том, как выражение генератора совпадает с обычным способом генератора, но это, похоже, не так, и другие темы, которые я «Мы читаем, что выражение генератора vs yield говорит, что нет никакой разницы. Однако из того, что я вижу, используя выход, будет возвращаться в цикл for каждый раз, когда выражение генератора не работает. Он завершает свою задачу, а затем возвращается в цикл for. Это может быть довольно большая разница в использовании памяти (в зависимости от того, что вы зацикливаете) правильно? Я прав, думая?Выражения генератора vs yield

# 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

Ваш оператор 'print()' находится в неправильном месте в 'evens'. Он должен быть в начале, а не в цикле. –

+0

Ваше выражение генератора и функция генератора ведут себя точно так же * в противном случае. Оба дают 'n' только при итерации. –

ответ

4

Вы ошибаетесь в своем мышлении. Ваше выражение генератора делает точно то же самое, что и функция генератора, только с одной разницей: вы поместили print() в неправильное место. В evens2 вы печатаете до выполнено выражение генератора, создающее объект генератора, а в evens вы печатаете внутри самой функции генератора.

Если это Python 3 (или вы использовали from __future__ import print_function), вы можете использовать функцию print() внутри выражения генератора тоже:

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 

print() всегда возвращается None, поэтому print(..) or n вернет n. Итерация либо будет печататься, либо выводить все значения n.

Демо:

>>> 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 

Оба вызова производит объект генератора, и как генератор объектов печати дополнительной информации каждый раз, когда вы заранее их один шаг с next().

Что касается Python, то, что два объекта генератора производят более или менее такой же байт-код:

>>> 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 

Оба используют FOR_ITER в цикле, COMPARE_OP, чтобы увидеть, если выход BINARY_MODULO равно 0 и оба используют YIELD_VALUE, чтобы получить значение n.

+0

ty для объяснения. Я не был уверен, что могу написать там заявление о печати. Таким образом, выражения генератора просто скрывают оператор yield от нас. – user441521

+0

@ user441521: точно так же, как понимание списка скрывает вызов 'list.append()' от нас. –

Смежные вопросы