Сообщить об ошибке.

Цикл for/in в Python

Содержание:

Цикл for item in iterable: в Python.

Цикл for в Python немного отличается от того, что можно использовать в C или Pascal. for в Python не перебирает арифметическую прогрессию чисел, например как в Pascal, не дает пользователю возможность определять шаг итерации и условие остановки, как C. Вместо этого инструкция for в Python перебирает элементы любой итерируемой последовательности iterable (список list, строку string, кортеж tuple, словарь dict или другого объекта, поддерживающего итерацию) в том порядке, в котором они появляются.

>>> words = ['cat', 'window', 'defenestrate']
# проходимся по списку `words`
>>> for w in words:
...     print(w)
... 
# cat
# window
# defenestrate

Также внутри цикла можно извлекать элементы списка по индексу. Для этого определим количество элементов n в списке при помощи функции len(), а затем сгенерируем арифметическую последовательность range(n), которую будем перебирать:

>>> words = ['cat', 'window', 'defenestrate']
# определим количество элементов в списке
>>> n = len(words)
# перебираем последовательность `range(n)`
>>> for i in range(n):
        # извлекаем элементы списка
        # `words` по индексу `i`
...     print(words[i])
... 
# cat
# window
# defenestrate

Изменено в Python 3.11: Теперь в списке выражений разрешены элементы, отмеченные звездочкой (смотрите "Передача произвольного числа аргументов"). Эта возможность была доступна в версиях Python 3.9 и 3.10, официально задокументирована в Python 3.11.

for x in *a, *b:
    print(x)

Словарь Python - это такая же последовательность, которая имеет представления dict.items(), dict.keys(), dict.values() по которым можно итерироваться:

>>> d = {0: 'zero', 1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five'}
>>> for key, value in d.items():
...     print(key, ': ' ,value)
... 
# 0 :  zero
# 1 :  one
# 2 :  two
# 3 :  three
# 4 :  four
# 5 :  five

Код, который изменяет коллекцию (список, словарь и т.д.) во время итерации по этой же коллекции, может привести к неожиданному результату, совершенно не тому, который ожидался. Не делайте так НИКОГДА! Намного проще и безопаснее выполнить цикл над копией коллекции:

# допустим `users` - это словарь Python
# создаем копию словаря `users`
users_copy = users.copy()
# проходимся по копии словаря `users`
for user, status in users_copy.items():
    if status == 'inactive':
        # изменяем исходный словарь
        del users[user]

или создать новую коллекцию:

# создаем новый словарь
active_users = {}
# проходимся по словарю `users`
for user, status in users.items():
    # отбираем пользователей 
    if status == 'active':
        # добавляем в новый словарь 
        # отобранных пользователей
        active_users[user] = status

Спецификация оператора for/in/else.

for_stmt :: = "for" target_list "in" expression_list ":" suite
              ["else" ":" suite]

Список выражений для перебора инструкцией for вычисляется один раз и должен давать объект поддерживающий итерацию. Итератор создается для результата expression_list. Каждый элемент из expression_list в свою очередь присваивается целевой переменной target_list, значение которой передается в блок кода внутри инструкции for. Затем код блока for выполняется один раз для каждого элемента. Когда элементы исчерпаны, что происходит сразу же, когда последовательность пуста или итератор вызывает исключение StopIteration, выполняется набор в предложении else, если он присутствует, и цикл завершается.

  • Оператор break: выполняется код внутри for до оператора break и завершает цикл без выполнения блока внутри else.
  • Оператор continue: выполняется код внутри for до оператора continue, пропускает оставшуюся часть кода в блоке for и продолжает работу со следующим элементом списка перебираемых выражений или с оператором else, если следующего элемента нет.

Применим оператор break и continue в коде for/in/else и посмотрим на их поведение. Будем создавать список четных чисел из последовательности чисел от 0 до 14.

lst = []
for item in range(15) :
    # если число 10 есть в списке
    if 10 in lst:
        # прерываем цикл, при этом блок else не выполнится
        break
    # остаток от деления элемента списка
    a = item % 2
    # если элемент списка не четный или равен 0
    if a != 0 or item == 0:
        # пропускаем оставшийся код
        continue
    # добавление числа в список
    lst.append(item)
else:
    print ("Напечатает, если убрать условие с break")

print(lst)
# Код выведет:
[2, 4, 6, 8, 10]

Цикл for выполняет назначения переменным в целевом списке. Это перезаписывает все предыдущие назначения этим переменным, включая те, которые были сделаны в блоке for-loop:

Пример:

for i in range(10):
    print(i)
    i = 5
    # это не повлияет на цикл for так как переменная i
    # будет перезаписана следующим итерируемым элементом

Имена в целевом списке не удаляются, когда цикл завершен, но если последовательность пуста, то код внутри цикла не будет выполнен.

Подсказка: встроенная функция range() возвращает итератор целых чисел, подходящий для эмуляции эффекта языка Pascal for i: = a to b do; например, list(range(3)) возвращает список [0, 1, 2].

Изменено в Python 3.11: Теперь в списке выражений разрешены элементы, отмеченные звездочкой (смотрите "Передача произвольного числа аргументов"). Эта возможность была доступна в версиях Python 3.9 и 3.10, официально задокументирована в Python 3.11.

for x in *a, *b:
    print(x)

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

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

b = a[:]
for item in b:
    if item < 0: 
        a.remove(item)