Это скорее обратное тому, для чего вы можете использовать функции генератора Python? : генераторы python, выражения генератора и itertools
модуль - одни из моих любимых функций python в наши дни. Они особенно полезны при настройке цепочек операций для работы с большим объемом данных - я часто использую их при обработке файлов DSV.
Так когда же не самое подходящее время использовать генератор, выражение генератора или itertools
функцию?
- Когда я должен предпочесть
zip()
большеitertools.izip()
, или range()
надxrange()
, или[x for x in foo]
закончился(x for x in foo)
?
Очевидно, что в конечном итоге нам нужно «преобразовать» генератор в фактические данные, обычно путем создания списка или перебора его с помощью цикла без генератора. Иногда нам просто нужно знать длину. Я не об этом спрашиваю.
Мы используем генераторы, чтобы не назначать новые списки в память для промежуточных данных. Это особенно важно для больших наборов данных. Имеет ли это смысл и для небольших наборов данных? Есть ли заметный компромисс между памятью и процессором?
Мне особенно интересно, проводил ли кто-нибудь какое-то профилирование по этому поводу, в свете открывающего глаза обсуждения производительности понимания списка по сравнению с map () и filter () . ( альтернативная ссылка )
источник
<5
.Ответы:
Используйте список вместо генератора, когда:
1) Вам необходимо получить доступ к данным несколько раз (т.е. кешировать результаты, а не пересчитывать их):
for i in outer: # used once, okay to be a generator or return a list for j in inner: # used multiple times, reusing a list is better ...
2) Вам нужен произвольный доступ (или любой доступ, кроме прямого последовательного порядка):
for i in reversed(data): ... # generators aren't reversible s[i], s[j] = s[j], s[i] # generators aren't indexable
3) Вам необходимо соединить строки (для чего требуется два прохода данных):
s = ''.join(data) # lists are faster than generators in this use case
4) Вы используете PyPy, который иногда не может оптимизировать код генератора настолько, насколько это возможно, с помощью обычных вызовов функций и манипуляций со списком.
источник
ireduce
для репликации соединения?''.join('%s' % i for i in xrange(10))
В общем, не используйте генератор, когда вам нужны операции со списком, такие как len (), reversed () и т. Д.
Также могут быть случаи, когда вам не нужна ленивая оценка (например, чтобы выполнить все вычисления заранее, чтобы вы могли освободить ресурс). В этом случае может быть лучше выражение списка.
источник
Профиль, Профиль, Профиль.
Профилирование кода - единственный способ узнать, имеет ли вообще то, что вы делаете, какой-либо эффект.
Большинство случаев использования xrange, генераторов и т. Д. Превышают статический размер, небольшие наборы данных. Только когда вы добираетесь до больших наборов данных, это действительно имеет значение. range () vs. xrange () в основном заключается в том, чтобы сделать код немного более уродливым, и ничего не потерять, а, возможно, что-то получить.
Профиль, Профиль, Профиль.
источник
Вы никогда не должны способствовать
zip
болееizip
,range
болееxrange
или списочным более постижениям генератора. В Python 3.0range
имеетxrange
-как семантики иzip
имеетizip
-как семантики.Понимание списков на самом деле более ясное, как
list(frob(x) for x in foo)
в тех случаях, когда вам нужен реальный список.источник
for
циклы!), Но можно легко написать непонятные понимания списков.[]
форма достаточно описательна (и в целом более лаконична и менее загромождена). Но это дело вкуса.Как вы упомянули: «Это особенно важно для больших наборов данных», я думаю, это отвечает на ваш вопрос.
Если вы не бьетесь ни о чем, с точки зрения производительности вы все равно можете придерживаться списков и стандартных функций. Затем, когда у вас возникнут проблемы с производительностью, переключитесь.
Однако, как упоминалось @ u0b34a0f6ae в комментариях, использование генераторов в начале может облегчить вам масштабирование до более крупных наборов данных.
источник
Что касается производительности: при использовании psyco списки могут быть немного быстрее, чем генераторы. В приведенном ниже примере списки почти на 50% быстрее при использовании psyco.full ().
import psyco import time import cStringIO def time_func(func): """The amount of time it requires func to run""" start = time.clock() func() return time.clock() - start def fizzbuzz(num): """That algorithm we all know and love""" if not num % 3 and not num % 5: return "%d fizz buzz" % num elif not num % 3: return "%d fizz" % num elif not num % 5: return "%d buzz" % num return None def with_list(num): """Try getting fizzbuzz with a list comprehension and range""" out = cStringIO.StringIO() for fibby in [fizzbuzz(x) for x in range(1, num) if fizzbuzz(x)]: print >> out, fibby return out.getvalue() def with_genx(num): """Try getting fizzbuzz with generator expression and xrange""" out = cStringIO.StringIO() for fibby in (fizzbuzz(x) for x in xrange(1, num) if fizzbuzz(x)): print >> out, fibby return out.getvalue() def main(): """ Test speed of generator expressions versus list comprehensions, with and without psyco. """ #our variables nums = [10000, 100000] funcs = [with_list, with_genx] # try without psyco 1st print "without psyco" for num in nums: print " number:", num for func in funcs: print func.__name__, time_func(lambda : func(num)), "seconds" print # now with psyco print "with psyco" psyco.full() for num in nums: print " number:", num for func in funcs: print func.__name__, time_func(lambda : func(num)), "seconds" print if __name__ == "__main__": main()
Полученные результаты:
without psyco number: 10000 with_list 0.0519102208309 seconds with_genx 0.0535933367509 seconds number: 100000 with_list 0.542204280744 seconds with_genx 0.557837353115 seconds with psyco number: 10000 with_list 0.0286369007033 seconds with_genx 0.0513424889137 seconds number: 100000 with_list 0.335414877839 seconds with_genx 0.580363490491 seconds
источник
Что касается производительности, я не могу вспомнить ни одного случая, когда вы хотели бы использовать список вместо генератора.
источник
all(True for _ in range(10 ** 8))
медленнее, чемall([True for _ in range(10 ** 8)])
в Python 3.8. Я бы предпочел здесь список, а не генераторЯ никогда не встречал ситуации, когда генераторы мешали бы тому, что вы пытаетесь сделать. Однако есть множество случаев, когда использование генераторов не поможет вам больше, чем их неиспользование.
Например:
sorted(xrange(5))
Не предлагает никаких улучшений по сравнению с:
sorted(range(5))
источник
range(5)
, так как результирующий список уже отсортирован.Вы должны предпочесть понимание списков, если вам нужно сохранить значения для чего-то еще позже, и размер вашего набора не слишком велик.
Например: вы создаете список, который в дальнейшем будете повторять несколько раз в своей программе.
В некоторой степени вы можете думать о генераторах как о замене итераций (циклов), а не понимания списков как о типе инициализации структуры данных. Если вы хотите сохранить структуру данных, используйте списки.
источник
itertools.tee()
может вам помочь. Но, как правило, если вам нужно более одного прохода или произвольный доступ к некоторым промежуточным данным, сделайте их список / набор / определение.