Присоединение к списку:
>>> ''.join([ str(_) for _ in xrange(10) ])
'0123456789'
join
должен принимать итерацию.
Видимо, join
аргумент есть [ str(_) for _ in xrange(10) ]
, и это понимание списка .
Посмотри на это:
>>>''.join( str(_) for _ in xrange(10) )
'0123456789'
Теперь join
аргумент «s просто str(_) for _ in xrange(10)
, нет []
, но результат тот же.
Почему? Имеет str(_) for _ in xrange(10)
также составить список или итератор?
python
list-comprehension
Олкотт
источник
источник
join
это, скорее всего, написано на C и поэтому работает намного быстрее, чем понимание списка ... Время тестирования!_
не имеет особого значения, это обычное имя переменной. Его часто используют как одноразовое имя, но это не так (вы используете переменную). Я бы избегал использовать это в коде (по крайней мере, таким образом).Ответы:
>>>''.join( str(_) for _ in xrange(10) )
Это называется выражением генератора и объясняется в PEP 289 .
Основное различие между выражениями генератора и пониманием списков состоит в том, что первые не создают список в памяти.
Обратите внимание, что есть третий способ написать выражение:
''.join(map(str, xrange(10)))
источник
( str(_) for _ in xrange(10) )
. Но я был сбит с толку, почему()
можно опуститьjoin
, что означает, что код должен быть похож на `` '' .join ((str (_) for _ in xrange (10))), верно?tup = 1, 2, 3; print(tup)
. Имея это в виду, использованиеfor
в качестве части выражения создает генератор, а скобки служат только для того, чтобы отличить его от неправильно написанного цикла.Другие респонденты правильно ответили, что вы обнаружили выражение-генератор (которое имеет обозначение, подобное пониманию списков, но без квадратных скобок).
В целом, genexps (так их ласково называют) более эффективны с точки зрения памяти и быстрее, чем составление списков.
ОДНАКО, в этом случае
''.join()
понимание списка происходит быстрее и эффективнее с точки зрения памяти. Причина в том, что объединению необходимо выполнить два прохода по данным, поэтому ему действительно нужен реальный список. Если вы дадите ему один, он сразу же начнет свою работу. Если вместо этого вы дадите ему genexp, он не сможет начать работу до тех пор, пока не создаст новый список в памяти, запустив genexp до исчерпания:~ $ python -m timeit '"".join(str(n) for n in xrange(1000))' 1000 loops, best of 3: 335 usec per loop ~ $ python -m timeit '"".join([str(n) for n in xrange(1000)])' 1000 loops, best of 3: 288 usec per loop
Тот же результат сохраняется при сравнении itertools.imap с картой :
~ $ python -m timeit -s'from itertools import imap' '"".join(imap(str, xrange(1000)))' 1000 loops, best of 3: 220 usec per loop ~ $ python -m timeit '"".join(map(str, xrange(1000)))' 1000 loops, best of 3: 212 usec per loop
источник
''.join()
для построения строки требуется 2 прохода через итератор?Во втором примере используется выражение генератора, а не понимание списка. Разница в том, что при понимании списка список полностью создается и передается в
.join()
. С помощью выражения генератора элементы генерируются один за другим и потребляются.join()
. Последний использует меньше памяти и обычно работает быстрее.Как это бывает, конструктор списка с радостью использует любую итерацию, включая выражение генератора. Так:
[str(n) for n in xrange(10)]
просто "синтаксический сахар" для:
list(str(n) for n in xrange(10))
Другими словами, понимание списка - это просто выражение генератора, которое превращается в список.
источник
[str(x) for x in xrange(1000)]
262 мкс,:list(str(x) for x in xrange(1000))
304 мкс.seq = PySequence_Fast(orig, "");
это единственная причина, по которой итераторы работают медленнее, чем списки или кортежи при вызове str.join (). Вы можете начать чат, если хотите обсудить это дальше (я автор PEP 289, создатель кода операции LIST_APPEND и тот, кто оптимизировал конструктор list (), так что у меня есть знакомство с вопросом).Как уже упоминалось, это выражение-генератор .
Из документации:
источник
Если оно заключено в круглые, но не квадратные скобки, технически это выражение генератора. Генераторные выражения были впервые представлены в Python 2.4.
http://wiki.python.org/moin/Generators
Часть после соединения
( str(_) for _ in xrange(10) )
сама по себе является выражением генератора. Вы можете сделать что-то вроде:mylist = (str(_) for _ in xrange(10)) ''.join(mylist)
и это означает то же самое, что вы написали во втором случае выше.
Генераторы обладают некоторыми очень интересными свойствами, не последним из которых является то, что они не выделяют весь список, когда он вам не нужен. Вместо этого такая функция, как join, «выкачивает» элементы из выражения генератора по одному, выполняя свою работу с крошечными промежуточными частями.
В ваших конкретных примерах список и генератор, вероятно, не работают по-разному, но в целом я предпочитаю использовать выражения генератора (и даже функции генератора) всякий раз, когда могу, в основном потому, что крайне редко генератор работает медленнее, чем полный список материализация.
источник
Это генератор, а не понимание списка. Генераторы также являются итерабельными, но вместо того, чтобы сначала создать весь список, а затем передать его для соединения, он передает каждое значение в xrange одно за другим, что может быть намного более эффективным.
источник
Аргументом вашего второго
join
вызова является выражение генератора. Это создает итерацию.источник