Когда не самое подходящее время для использования генераторов python?
это скорее наоборот для чего можно использовать функции генератора Python?: генераторы python, выражения генератора и itertools модуль-это некоторые из моих любимых функций python в эти дни. Они особенно полезны при настройке цепочек операций для выполнения на большой куче данных-я часто использую их при обработке файлов DSV.
так когда же это не хорошее время для использования генератора, или выражения генератора, или ?
- когда я должен предпочесть
zip()overitertools.izip()или
range()overxrange()или
[x for x in foo]over(x for x in foo)?
мы используем генераторы, так что мы не назначение новых списков в память для промежуточных данных. Особенно это имеет смысл для больших наборов данных. Имеет ли смысл и для небольших наборов данных? Есть ли заметный компромисс между памятью и процессором?
мне особенно интересно, если кто-то сделал некоторые профилирования на этом, в свете открывающего глаза обсуждения представление понимания списка против карты () и фильтра (). ( alt link)
9 ответов:
использовать список вместо генератора, когда:
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 indexable3) Вам нужно вступить строки (которые требуют двух проходов над данными):
s = ''.join(data) # lists are faster than generators in this use case4) Вы используете пользователей который иногда не может оптимизировать код генератора столько, сколько он может с обычными вызовами функций и манипуляциями со списком.
В общем, не используйте генератор, когда вам нужны операции со списком, такие как len(), reversed () и т. д.
также могут быть случаи, когда вам не нужна ленивая оценка (например, чтобы выполнить все вычисления заранее, чтобы вы могли освободить ресурс). В этом случае выражение списка может быть лучше.
Профиль, Профиль, Профиль.
профилирование кода-это единственный способ узнать, имеет ли то, что вы делаете, какой-либо эффект.
большинств использования xrange, генераторов, etc над статическим размером, малыми наборами данных. Только когда вы получаете большие наборы данных, это действительно имеет значение. диапазон() и xrange() в основном просто сделать код выглядеть немного более уродливый, и, ничего не теряя, и, возможно, набирает что-то.
Профиль, Профиль, Профиль.
что касается производительности: при использовании psyco, списки могут быть довольно немного быстрее, чем Генераторы. В приведенном ниже примере списки почти на 50% быстрее при использовании psyco.полный()
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
Как вы упомянули ," это особенно имеет смысл для больших наборов данных", я думаю, что это отвечает на ваш вопрос.
Если вы не ударяете по стенам, по производительности, вы все равно можете придерживаться списков и стандартных функций. Затем, когда вы столкнетесь с проблемами производительности коммутатора.
Как упоминалось @u0b34a0f6ae в комментариях, однако, использование генераторов в начале может облегчить вам масштабирование до больших наборов данных.
вы должны предпочесть понимание списка, если вам нужно сохранить значения вокруг для чего-то еще позже, и размер вашего набора не слишком велик.
например: вы создаете список, который вы будете перебирать несколько раз позже в вашей программе.
в какой-то степени вы можете думать о генераторах как о замене итераций (циклов) против понимания списка как типа инициализации структуры данных. Если вы хотите сохранить структуру данных, то используйте список понимания.
Что касается производительности, я не могу думать любой раз, что вы хотели бы использовать список генератор.
Я никогда не встречал ситуации, когда генераторы мешали бы тому, что вы пытаетесь сделать. Есть, однако, много случаев, когда использование генераторов не поможет вам больше, чем не использовать их.
например:
sorted(xrange(5))не предлагает никаких улучшений по сравнению с:
sorted(range(5))
Comments