Python вложенный цикл for против генератора для многократного перебора списка, предоставляемого функцией

#for-loop #iterator #python

#for-цикл #итератор #python

Вопрос:

Проблема довольно проста, у меня есть метод, который возвращает список. Я хочу перебрать каждый элемент в этом списке, а после завершения вызвать метод для получения нового списка и повторить.

На данный момент мой код выглядит примерно так:

 generator = iter([])
while Condition:
    try:
        item = next(generator)
    except StopIteration:
        generator = iter(list_returining_method())
        item = next(generator)
    ...
 

Однако ранее я использовал вложенный for цикл.

 while Condition:
    for item in list_returining_method():
        ...
 

Хотя моя предыдущая попытка выглядит лучше в некоторых отношениях, но мой текущий метод имеет некоторые «преимущества»:

  • если для условия установлено значение false, цикл завершается без необходимости break выхода из цикла for.
    • в дополнение к вышеуказанным причинам методы с доступом к условию могут завершить цикл, не просматривая все другие элементы в указанном списке или не выполняя специальную проверку в цикле for.
  • Первый подход позволяет пропускать элементы в цикле, если возникнет необходимость.
  • Существует также на один уровень отступа меньше. Это больше тщеславия, чем что-либо еще, но, учитывая, что фактический код был частью метода класса, уровень отступа уже был довольно высоким.

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

Ответ №1:

Я бы посоветовал вам использовать некоторые итераторы

 items = itertools.chain.from_iterable( iter(list_returning_method, None) )

for item in items:
    # do something with item
    print item

    if not Condition:
       break
 

iter(callable, sentinel) возвращает итератор, который выдает результат callable(), пока не вернет sentinel .

itertools.chain.from_iterable возвращает итератор, который выравнивает исходный итератор, он будет создавать значения в списках, созданных первым итератором.

Я думаю, что это дает вам большинство преимуществ вашего метода, но с более чистым стилем.

Комментарии:

1. Это работает очень хорошо. Хотя первая строка изначально выглядела довольно загадочно, это гораздо лучшее решение. Единственная проблема, которую я вижу в этом, заключается в том, что передача аргументов вызываемому объекту необходима. Любые предложения о том, как решить эту проблему?

2. @SingularityCat, есть два варианта. Вы можете использовать functools.partial, который объединяет функцию и аргументы для создания новой функции. Или вы можете написать генератор.

Ответ №2:

Здесь есть место для мнений, но краткость второго подхода делает его явным выигрышем, насколько я понимаю. Особенно, если использование item переменной ограничено внутри цикла, я бы каждый раз выбирал синтаксис for / in . Были некоторые проблемы, связанные с буферами и изменениями состояния в цикле, где я решил не использовать for / in . Но это небольшое меньшинство.