Python: выражение генератора по сравнению с урожаем

В Python, там какое-либо различие между созданием объекта генератора посредством выражения генератора по сравнению с использованием оператора урожая?

Используя урожай:

def Generator(x, y):
    for i in xrange(x):
        for j in xrange(y):
            yield(i, j)

Используя выражение генератора:

def Generator(x, y):
    return ((i, j) for i in xrange(x) for j in xrange(y))

Обе функции возвращают объекты генератора, которые производят кортежи, например, (0,0), (0,1) и т.д.

Какие-либо преимущества одного или другого? Мысли?


Спасибо все! Существует большая большая информация и дальнейшие ссылки в этих ответах!

82
задан cschol 3 January 2010 в 18:17
поделиться

6 ответов

Есть только небольшие различия в двух. Вы можете использовать модуль DIS , чтобы проверить эту вещь для себя.

Редактировать: Моя первая версия декомпилировала выражение генератора, созданное при области модуля в интерактивной подсказке. Это немного отличается от версии OP, с ней использовали внутри функции. Я изменил это, чтобы соответствовать фактическому случаю в этом вопросе.

Как вы можете видеть ниже, генератор «Выход» (первый случай) имеет три дополнительных инструкции в настройке, но с первого for_iter они отличаются только одним уважением: «Доходность» подход использует A Load_Fast вместо load_deref внутри цикла. Load_Deref «Скорее медленнее» , чем Load_Fast , поэтому он делает версию «доходность» немного быстрее, чем выражение генератора для больших достаточном значений x (внешний цикл), поскольку значение y загружено немного быстрее на каждом проходе. Для более мелких значений x Было бы немного медленнее из-за дополнительного надлога кода установки.

Также стоит указать, что выражение генератора обычно будет использоваться встроенный в код, а не упаковывать его с таким функцией. Это будет удалять немного настроек настроек и слегка ускорить выражение генератора для меньшего количества значений петли, даже если Load_Fast дала версию «Выход» преимущество в противном случае.

Ни в одном случае не будет недостаточным значением производительности, чтобы оправдать принятие решения между одним или другим. Читаемость рассчитывается гораздо больше, поэтому используйте, в зависимости от того, что чувствует себя наиболее читаемыми для ситуации под рукой.

>>> def Generator(x, y):
...     for i in xrange(x):
...         for j in xrange(y):
...             yield(i, j)
...
>>> dis.dis(Generator)
  2           0 SETUP_LOOP              54 (to 57)
              3 LOAD_GLOBAL              0 (xrange)
              6 LOAD_FAST                0 (x)
              9 CALL_FUNCTION            1
             12 GET_ITER
        >>   13 FOR_ITER                40 (to 56)
             16 STORE_FAST               2 (i)

  3          19 SETUP_LOOP              31 (to 53)
             22 LOAD_GLOBAL              0 (xrange)
             25 LOAD_FAST                1 (y)
             28 CALL_FUNCTION            1
             31 GET_ITER
        >>   32 FOR_ITER                17 (to 52)
             35 STORE_FAST               3 (j)

  4          38 LOAD_FAST                2 (i)
             41 LOAD_FAST                3 (j)
             44 BUILD_TUPLE              2
             47 YIELD_VALUE
             48 POP_TOP
             49 JUMP_ABSOLUTE           32
        >>   52 POP_BLOCK
        >>   53 JUMP_ABSOLUTE           13
        >>   56 POP_BLOCK
        >>   57 LOAD_CONST               0 (None)
             60 RETURN_VALUE
>>> def Generator_expr(x, y):
...    return ((i, j) for i in xrange(x) for j in xrange(y))
...
>>> dis.dis(Generator_expr.func_code.co_consts[1])
  2           0 SETUP_LOOP              47 (to 50)
              3 LOAD_FAST                0 (.0)
        >>    6 FOR_ITER                40 (to 49)
              9 STORE_FAST               1 (i)
             12 SETUP_LOOP              31 (to 46)
             15 LOAD_GLOBAL              0 (xrange)
             18 LOAD_DEREF               0 (y)
             21 CALL_FUNCTION            1
             24 GET_ITER
        >>   25 FOR_ITER                17 (to 45)
             28 STORE_FAST               2 (j)
             31 LOAD_FAST                1 (i)
             34 LOAD_FAST                2 (j)
             37 BUILD_TUPLE              2
             40 YIELD_VALUE
             41 POP_TOP
             42 JUMP_ABSOLUTE           25
        >>   45 POP_BLOCK
        >>   46 JUMP_ABSOLUTE            6
        >>   49 POP_BLOCK
        >>   50 LOAD_CONST               0 (None)
             53 RETURN_VALUE
70
ответ дан 24 November 2019 в 09:15
поделиться

Нет никакой разницы между простыми циклами, которые можно вписать в выражение генератора. Однако доходность может быть использована для создания генераторов, которые делают гораздо более сложную обработку. Приведем простой пример генерации последовательности Фибоначчи:

>>> def fibgen():
...    a = b = 1
...    while True:
...        yield a
...        a, b = b, a+b

>>> list(itertools.takewhile((lambda x: x<100), fibgen()))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
18
ответ дан 24 November 2019 в 09:15
поделиться

В этом примере, не на самом деле. Но Урожай может быть использован для более сложных конструкций - , например , он также может принимать значения от вызывающего абонента и изменять поток в результате. Читать PEP 342 Для более подробной информации (это интересная техника, которую стоит знать).

В любом случае, лучший совет - это Использование всего, что для ваших нужд более понятнее .

P.S. Вот простой пример COROUTINE из Dave Beazley :

def grep(pattern):
    print "Looking for %s" % pattern
    while True:
        line = (yield)
        if pattern in line:
            print line,

# Example use
if __name__ == '__main__':
    g = grep("python")
    g.next()
    g.send("Yeah, but no, but yeah, but no")
    g.send("A series of tubes")
    g.send("python generators rock!")
35
ответ дан 24 November 2019 в 09:15
поделиться

Использование yield хорошо, если выражение сложнее, чем просто вложенные циклы. Кроме всего прочего, можно вернуть специальное первое или специальное последнее значение. Consider:

def Generator(x):
  for i in xrange(x):
    yield(i)
  yield(None)
8
ответ дан 24 November 2019 в 09:15
поделиться

Если подумать об итераторах, то модуль itertools:

... стандартизирует набор быстрых, эффективных с точки зрения памяти инструментов, которые полезны как сами по себе, так и в комбинации. Вместе они образуют "алгебру итераторов", позволяющую лаконично и эффективно строить специализированные инструменты на чистом питоне.

Для оценки производительности рассмотрим itertools.product(*iterables[, repeat])

Cartesian product of input iterables.

Эквивалент вложенных for-loops в выражении генератора. Например, продукт(A, B) возвращает то же самое, что ((x,y) для x в A для y в B).

>>> import itertools
>>> def gen(x,y):
...     return itertools.product(xrange(x),xrange(y))
... 
>>> [t for t in gen(3,2)]
[(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)]
>>> 
5
ответ дан 24 November 2019 в 09:15
поделиться

Лично мне нравится признание IEC 60559: 1989 (двоичная арифметика с плавающей точкой для микропроцессорных систем. ) и гораздо лучшая подставка с плавающей точкой.

В аналогичной вене, установке и запросе режима округления с плавающей точкой, проверка на нэн / бесконечность / субнормальные номера и т. Д., Здорово иметь.

-121--833649-

При использовании, обратите внимание на различие между объектом генератора против функции генератора.

Объект генератора используется - только один раз, в отличие от функции генератора, которую можно повторно использовать каждый раз, когда вы вызываете его снова, потому что он возвращает объект свежего генератора.

Экспрессии генератора находятся на практике обычно используются «RAW», не упаковывая их в функцию, и они возвращают объект генератора.

Например:

def range_10_gen_func():
    x = 0
    while x < 10:
        yield x
        x = x + 1

print(list(range_10_gen_func()))
print(list(range_10_gen_func()))
print(list(range_10_gen_func()))

, какие выходы:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Сравните со слегка разным использованием:

range_10_gen = range_10_gen_func()
print(list(range_10_gen))
print(list(range_10_gen))
print(list(range_10_gen))

, которые выводы:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[]
[]

и сравнение с выражением генератора:

range_10_gen_expr = (x for x in range(10))
print(list(range_10_gen_expr))
print(list(range_10_gen_expr))
print(list(range_10_gen_expr))

, который также выводит:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[]
[]
9
ответ дан 24 November 2019 в 09:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: