Подтвердить что ты не робот

Почему xrange способен вернуться к началу в Python?

Я столкнулся с этим кодом из Большинство pythonic способов подсчета совпадающих элементов в чем-то итерабельном

r = xrange(1, 10)
print sum(1 for v in r if v % 2 == 0) # 4
print sum(1 for v in r if v % 3 == 0) # 3

r повторяется один раз. и затем снова повторили. Я подумал, что если итератор один раз потребляется, тогда он снова и снова не должен повторяться.

Выражения генератора могут повторяться только один раз:

r = (7 * i for i in xrange(1, 10))
print sum(1 for v in r if v % 2 == 0) # 4
print sum(1 for v in r if v % 3 == 0) # 0

перечисление (L) тоже:

r = enumerate(mylist)

и объект файла:

f = open(myfilename, 'r')

Почему xrange ведет себя по-другому?

4b9b3361

Ответ 1

Поскольку объект xrange, созданный вызовом xrange(), указывает __iter__, который предоставляет уникальную версию самого себя (фактически, отдельный объект rangeiterator) каждый раз, когда он повторяется.

>>> x = xrange(3)
>>> type(x)
<type 'xrange'>
>>> i = x.__iter__()
>>> type(i)
<type 'rangeiterator'>

Ответ 2

Потому что xrange не возвращает генератор. Он возвращает объект xrange.

>>> type(xrange(10))
<type 'xrange'>

В дополнение к повторной итерации объекты xrange поддерживают другие вещи, которые не имеют генераторы - например, индексирование:

>>> xrange(10)[5]
5

Они также имеют длину:

>>> len(xrange(10))
10

И они могут быть отменены:

>>> list(reversed(xrange(10)))
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

Короче говоря, xrange объекты реализуют полный sequence интерфейс:

>>> import collections
>>> isinstance(xrange(10), collections.Sequence)
True

Они просто делают это, не используя много памяти.

Обратите внимание также, что в Python 3 объект range, возвращаемый range, имеет все те же свойства.

Ответ 3

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

Как объяснили senderle и Amber, конкретные итераторы, которые вы получаете, вызывая xrange, реализуются так, что вы можете перебирать их несколько раз.

Общая идея итератора допускает, что итераторы могут быть исчерпаны после повторения. Это связано с тем, что многие итераторы (такие как генераторы, обход файлов и т.д.) Будут трудно реализовать или потребляют гораздо больше памяти или работают намного медленнее, если им приходится поддерживать произвольно много обходов, и очень часто эта функциональность даже не будет используемый. Поэтому, если итераторы должны были поддерживать произвольно много обходов, то эти вещи, вероятно, не были бы итераторами.

Короче говоря, если вы пишете код, который работает на произвольном неизвестном итераторе, вы предполагаете, что его можно пропустить только один раз, и не имеет значения, дает ли кто-нибудь объект, который поддерживает больше, чем те функции, которые вам нужны, Если вы знаете некоторую дополнительную информацию об итераторе (например, это также последовательность или даже то же, что и объект xrange), тогда вы можете использовать код, чтобы использовать это, если хотите.