8 ответов:
ответ Джона хорош (что понимание списка лучше, когда вы хотите повторить что-то несколько раз). Однако также стоит отметить, что вы должны использовать список, если хотите использовать любой из методов списка. Например, следующий код не будет работать:
def gen(): return (something for something in get_some_stuff()) print gen()[:2] # generators don't support indexing or slicing print [5,6] + gen() # generators can't be added to listsв принципе, используйте выражение генератора, если все, что вы делаете, повторяется один раз. Если вы хотите сохранить и использовать сгенерированные результаты, то вам, вероятно, лучше со списком понимание.
переборе выражение генератор или понимание будет делать то же самое. Тем не менее,понимание сначала создаст весь список в памяти, пока выражение генератор будет создавать элементы на лету, так что вы можете использовать его для очень больших (а также бесконечных!) последовательности.
используйте понимание списка, когда результат должен быть повторен несколько раз, или где скорость имеет первостепенное значение. Используйте выражения генератора, где диапазон большой или бесконечный.
важным моментом является то, что восприятие списка создает новый список. Генератор создает итерационный объект, который будет" фильтровать " исходный материал на лету, когда вы потребляете биты.
представьте, что у вас есть файл журнала 2 ТБ под названием "hugefile.txt", и вы хотите содержание и длину для всех строк, которые начинаются со слова"запись".
Итак, вы пытаетесь начать с написания списка понимания:
logfile = open("hugefile.txt","r") entry_lines = [(line,len(line)) for line in logfile if line.startswith("ENTRY")]это хлебает весь файл, обрабатывает каждую строку и сохраняет соответствующие строки в массиве. Таким образом, этот массив может содержать до 2 ТБ контента. Это много оперативной памяти, и, вероятно, не практично для ваших целей.
поэтому вместо этого мы можем использовать генератор, чтобы применить "фильтр" к нашему контенту. Никакие данные фактически не считываются, пока мы не начнем повторять результат.
logfile = open("hugefile.txt","r") entry_lines = ((line,len(line)) for line in logfile if line.startswith("ENTRY"))еще ни одна строка не была прочитана из нашего файла. На самом деле, скажем, мы хотим отфильтровать наш результат даже далее:
long_entries = ((line,length) for (line,length) in entry_lines if length > 80)по-прежнему ничего не было прочитано, но мы указали теперь два генератора, которые будут действовать на наших данных, как мы хотим.
давайте напишем наши отфильтрованные строки в другой файл:
outfile = open("filtered.txt","a") for entry,length in long_entries: outfile.write(entry)теперь мы читаем входной файл. Как наши
forцикл продолжает запрашивать дополнительные строки,long_entriesгенератор требует линий отentry_linesгенератор, возвращающий только те, длина которых превышает 80 символов. А в свою очередь,entry_linesгенератор запрашивает строки (отфильтрованные как указано) изlogfileитератор, который в свою очередь читает файл.поэтому вместо того, чтобы" подталкивать "данные к вашей выходной функции в виде полностью заполненного списка, вы даете выходной функции способ" вытягивать " данные только тогда, когда это необходимо. Это в нашем случае гораздо эффективнее, но не совсем так гибко. Генераторы-это один путь, один проход; данные из файла журнала, который мы прочитали, сразу отбрасываются, поэтому мы не можем вернуться к a предыдущая строка. С другой стороны, нам не нужно беспокоиться о сохранении данных, как только мы закончим с этим.
преимущество выражения генератора заключается в том, что он использует меньше памяти, так как он не строит весь список сразу. Выражения генератора лучше всего использовать, когда список является посредником, например, суммирование результатов или создание диктанта из результатов.
например:
sum(x*2 for x in xrange(256)) dict( ((k, some_func(k) for k in some_list_of_keys) )преимущество заключается в том, что список не полностью генерируется, и поэтому используется мало памяти (и также должно быть быстрее)
вы должны, однако, использовать список понимание, когда желаемый конечный продукт-это список. Вы не собираетесь сохранять какие-либо мемы с помощью генераторных выражений, так как вам нужен сгенерированный список. Вы также получаете возможность использовать любую из функций списка, таких как сортировка или реверсирование.
например:
reversed( [x*2 for x in xrange(256)] )
при создании генератора из изменяемого объекта (например, списка) имейте в виду, что генератор будет оцениваться по состоянию списка во время использования генератора, а не во время создания генератора:
>>> mylist = ["a", "b", "c"] >>> gen = (elem + "1" for elem in mylist) >>> mylist.clear() >>> for x in gen: print (x) # nothingЕсли есть вероятность того, что ваш список будет изменен (или изменяемый объект внутри этого списка), но вам нужно состояние при создании генератора, вам нужно использовать понимание списка.
Я использую Hadoop Mincemeat module. Я думаю, что это отличный пример, чтобы принять к сведению:
import mincemeat def mapfn(k,v): for w in v: yield 'sum',w #yield 'count',1 def reducefn(k,v): r1=sum(v) r2=len(v) print r2 m=r1/r2 std=0 for i in range(r2): std+=pow(abs(v[i]-m),2) res=pow((std/r2),0.5) return r1,r2,resздесь генератор получает числа из текстового файла (размером до 15 ГБ) и применяет простую математику к этим числам, используя Hadoop map-reduce. Если бы я не использовал функцию yield, а вместо этого понимание списка, потребовалось бы гораздо больше времени для вычисления сумм и среднего (не говоря уже о сложности пространства).
Hadoop-это здорово пример использования всех преимуществ генераторов.
Comments