Ответ 1
Как ограничить итерации цикла в Python?
for index, item in enumerate(items): print(item) if index == limit: break
Есть ли более короткий, идиоматический способ написать выше? Как?
Включая индекс
zip
останавливается на кратчайшем итерабельном аргументе. (В отличие от поведения zip_longest
, который использует самый длинный итеративный.)
range
может обеспечить ограниченную итерабельность, которую мы можем передать zip вместе с нашим основным итерированием.
Итак, мы можем передать объект range
(с его аргументом stop
) в zip
и использовать его как ограниченное перечисление.
zip(range(limit), items)
Использование Python 3, zip
и range
возвращает итерации, в которых конвейер данных вместо материализации данных в списках для промежуточных шагов.
for _, item in zip(range(limit), items):
print(item)
Чтобы получить такое же поведение в Python 2, просто замените xrange
на range
и itertools.izip
на zip
.
from itertools import izip
for index, item in izip(xrange(limit), items):
print(item)
Если не требуется индекс, itertools.islice
Вы можете использовать itertools.islice
:
for item in itertools.islice(items, 0, stop):
print(item)
который не требует присвоения индекса.
Почему это не встроено в
enumerate
?
Здесь перечисление реализовано в чистом Python (с возможными изменениями для получения желаемого поведения в комментариях):
def enumerate(collection, start=0): # could add stop=None
i = start
it = iter(collection)
while 1: # could modify to `while i != stop:`
yield (i, next(it))
i += 1
Вышеуказанное будет менее эффективным для тех, кто уже использует перечисление, потому что ему нужно будет проверить, пора ли останавливаться на каждой итерации. Мы можем просто проверить и использовать старый enumerate, если не получить аргумент stop:
_enumerate = enumerate
def enumerate(collection, start=0, stop=None):
if stop is not None:
return zip(range(start, stop), collection)
return _enumerate(collection, start)
Эта дополнительная проверка будет иметь незначительное незначительное влияние на производительность.
Что касается того, почему перечисление не имеет аргумента stop, это было первоначально предложено (см. PEP 279):
Эта функция была первоначально предложена с дополнительным запуском и остановить аргументы. GvR [Guido van Rossum] указал, что вызов функции
enumerate(seqn, 4, 6)
имел альтернативную, правдоподобную интерпретацию как срез, который будет возвращать четвертый и пятый элементы последовательность. Чтобы избежать двусмысленности, необязательные аргументы были хотя это означало потеря гибкости в качестве счетчика циклов. Эта гибкость была наиболее важной для общего случая считая от одного, как в:for linenum, line in enumerate(source,1): print linenum, line
Таким образом, очевидно, что start
хранился, потому что он был очень ценным, а stop
был отброшен, поскольку он имел меньше случаев использования и способствовал путанице в использовании новой функции.
Избегайте нарезки с нотной записью
В другом ответе говорится:
Почему бы просто не использовать
for item in items[:limit]: # or limit+1, depends
Вот несколько минус:
- Он работает только для итераций, которые принимают разрез, поэтому он более ограничен.
- Если они принимают разрез, он обычно создает новую структуру данных в памяти, а не выполняет итерацию над структурой ссылочных данных, поэтому она отнимает память (все встроенные объекты делают копии при разрезе, но, например, массивы numpy создают просмотр при нарезке).
- Unsliceable iterables потребует другого вида обработки. Если вы переключитесь на ленивую оценочную модель, вам придется также изменить код с нарезкой.
Вы должны использовать разрез только с нотной надписью, когда понимаете ограничения и делаете ли это копию или представление.
Заключение
Я бы предположил, что теперь сообщество Python знает об использовании перечисления, затраты на беспорядок будут перевешиваться значением аргумента.
До этого времени вы можете использовать:
for index, element in zip(range(limit), items):
...
или, если вам не нужен индекс вообще:
for element in islice(items, 0, limit):
...
И избегайте нарезки с нотными обозначениями, если вы не понимаете ограничений.