шагом - бесконечный цикл python




Как я могу ограничить итерации цикла в Python? (4)

Как я могу ограничить итерации цикла в 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 index, item in zip(range(limit), items):
    print(index, 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(islice(items, stop)) для получения индекса

Как отмечает Пабло Руис Руис, мы можем также составить список с перечислением.

for index, item in enumerate(islice(items, limit)):
    print(index, 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

Вышеупомянутое было бы менее производительным для тех, кто уже использует перечисление, потому что это должно было бы проверить, пришло ли время остановить каждую итерацию. Мы можем просто проверить и использовать старое перечисление, если не получили аргумент 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)

Эта дополнительная проверка будет иметь незначительное влияние на производительность.

Что касается того, почему перечисление не имеет аргумента остановки, это было первоначально предложено (см. PEP 279 ):

Эта функция изначально была предложена с необязательными аргументами start и stop. 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

Вот несколько минусов:

  • Это работает только для итераций, которые принимают нарезку, поэтому оно более ограничено.
  • Если они допускают нарезку, обычно она создает новую структуру данных в памяти, вместо того, чтобы перебирать эталонную структуру данных, т. Е. Тратит память (все встроенные объекты копируют при нарезке, но, например, массивы с массивами создают представление при нарезке) ).
  • Unsliceable итерации требуют другого вида обработки. Если вы переключитесь на ленивую модель оценки, вам также придется изменить код с помощью нарезки.

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

Заключение

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

До этого времени вы можете использовать:

for index, element in zip(range(limit), items):
    ...

или же

for index, item in enumerate(islice(items, limit)):
    ...

или, если вам вообще не нужен индекс:

for element in islice(items, 0, limit):
    ...

И избегайте нарезки с помощью индексной записи, если вы не понимаете ограничений.

Скажем, у меня есть список элементов, и я хочу перебрать первые несколько из них:

items = list(range(10)) # I mean this to represent any kind of iterable.
limit = 5

Наивная реализация

Нафтон Python, пришедший из других языков, вероятно, напишет этот отлично работоспособный и производительный (если не однотипный) код:

index = 0
for item in items: # Python's `for` loop is a for-each.
    print(item)    # or whatever function of that item.
    index += 1
    if index == limit:
        break

Более идиоматическая реализация

Но в Python есть enumerate, который хорошо включает примерно половину этого кода:

for index, item in enumerate(items):
    print(item)
    if index == limit: # There's gotta be a better way.
        break

Итак, мы собрали дополнительный код пополам. Но должен быть лучший способ.

Можем ли мы приблизиться к описанному ниже поведению псевдокода?

Если бы enumerate принял другой необязательный аргумент stop (например, он принимает start аргумент вроде этого: enumerate(items, start=1) ), который, я думаю, был бы идеальным, но нижеприведенного не существует (см. Документацию по enumerate). здесь ):

# hypothetical code, not implemented:
for _, item in enumerate(items, start=0, stop=limit): # `stop` not implemented
    print(item)

Обратите внимание, что указывать имя index не нужно, поскольку нет необходимости ссылаться на него.

Есть ли идиоматический способ написать выше? Как?

Вторичный вопрос: почему это не встроено в перечисление?


Вы можете использовать itertools.islice для этого. Он принимает аргументы start , stop и step , если вы передаете только один аргумент, он считается stop . И это будет работать с любой итерацией.

itertools.islice(iterable, stop)
itertools.islice(iterable, start, stop[, step])

Демо - версия:

>>> from itertools import islice
>>> items = list(range(10))
>>> limit = 5
>>> for item in islice(items, limit):
    print item,
...
0 1 2 3 4

Пример из документов:

islice('ABCDEFG', 2) --> A B
islice('ABCDEFG', 2, 4) --> C D
islice('ABCDEFG', 2, None) --> C D E F G
islice('ABCDEFG', 0, None, 2) --> A C E G

Почему бы просто не использовать

for item in items[:limit]: # or limit+1, depends
    print(item)    # or whatever function of that item.

Это будет работать только для некоторых итераций, но так как вы указали списки, это работает.

Это не работает, если вы используете сеты или дикты и т. Д.


Пройдите остров с пределом внутри перечисления

a = [2,3,4,2,1,4]

for a, v in enumerate(islice(a, 3)): 
   print(a, v)

Выход:

0 2
1 3
2 4




enumerate