Цикл for

Цикл for в языке программирования Python предназначен для перебора элементов структур данных и некоторых других объектов. Это не цикл со счетчиком, каковым является for во многих других языках.

Что значит перебор элементов? Например, у нас есть список, состоящий из ряда элементов. Сначала берем из него первый элемент, затем второй, потом третий и так далее. С каждым элементом мы выполняем одни и те же действия в теле for. Нам не надо извлекать элементы по их индексам и заботится, на каком из них список заканчивается, и следующая итерация бессмысленна. Цикл for сам переберет и определит конец.

>>> spisok = [10, 40, 20, 30]
>>> for element in spisok:
...     print(element + 2)
... 
12
42
22
32

После ключевого слова for используется переменная под именем element. Имя здесь может быть любым. Нередко используют i. На каждой итерации цикла for ей будет присвоен очередной элемент из списка spisok. Так при первой прокрутке цикла идентификатор element связан с числом 10, на второй – с числом 40, и так далее. Когда элементы в spisok заканчиваются, цикл for завершает свою работу.

С английского "for" переводится как "для", "in" как "в". Перевести конструкцию с языка программирования на человеческий можно так: для каждого элемента в списке делать следующее (то, что в теле цикла).

В примере мы увеличивали каждый элемент на 2 и выводили его на экран. При этом сам список конечно же не изменялся:

>>> spisok
[10, 40, 20, 30]

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

>>> i = 0
>>> for element in spisok:
...     spisok[i] = element + 2
...     i += 1
... 
>>> spisok
[12, 42, 22, 32]

Но если мы вынуждены использовать счетчик, то выгода от использования цикла for не очевидна. Если знать длину списка, то почему бы не воспользоваться while. Длину можно измерить с помощью встроенной в Python функции len().

>>> i = 0
>>> while i < len(spisok):
...     spisok[i] = spisok[i] + 2 # или spisok[i] += 2
...     i = i + 1 # или i += 1
... 
>>> spisok
[14, 44, 24, 34]

Кроме того, с циклом while мы избавились от переменной element.

Функция range()

Теперь пришло время познакомиться со встроенной в Python функцией range(). "Range" переводится как "диапазон". Она может принимать один, два или три аргумента. Их назначение такое же как у функции randrange() из модуля random. Если задан только один, то генерируются числа от 0 до указанного числа, не включая его. Если заданы два, то числа генерируются от первого до второго, не включая его. Если заданы три, то третье число – это шаг.

Однако, в отличие от randrange(), функция range() генерирует не одно случайное число в указанном диапазоне. Она вообще не генерирует случайные числа. Она генерирует последовательность чисел в указанном диапазоне. Так, range(5, 11) сгенерирует последовательность 5, 6, 7, 8, 9, 10. Однако это будет не структура данных типа "список". Функция range() производит объекты своего класса – диапазоны:

>>> a = range(-10, 10)
>>> a
range(-10, 10)
>>> type(a)
<class 'range'>

Несмотря на то, что мы не видим последовательности чисел, она есть, и мы можем обращаться к ее элементам:

>>> a[0]
-10
>>> a[5]
-5
>>> a[15]
5
>>> a[-1]
9

Хотя изменять их нельзя, так как, в отличие от списков, объекты range() относятся к группе неизменяемых:

>>> a[10] = 100
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'range' object does not support item assignment

Цикл for и range()

Итак, зачем нам понадобилась функций range() в теме про цикл for? Дело в том, что вместе они образуют неплохой тандем. For как цикл перебора элементов, в отличие от while, позволяет не следить за тем, достигнут ли конец структуры. Не надо вводить счетчик для этого, изменять его и проверять условие в заголовке. С другой стороны, range() дает последовательность целых чисел, которые можно использовать как индексы для элементов того же списка.

>>> range(len(spisok))
range(0, 4)

Здесь с помощью функции len() измеряется длина списка. В данном случае она равна четырем. После этого число 4 передается в функцию range(), и она генерирует последовательность чисел от 0 до 3 включительно. Это как раз индексы элементов нашего списка.

Теперь "соединим" for и range():

>>> for i in range(len(spisok)):
...     spisok[i] += 2
... 
>>> spisok
[16, 46, 26, 36]

В заголовке цикла for берутся элементы вовсе не списка, а объекта range. Список, элементы которого планируется перезаписывать, тут по-сути не фигурирует. Если заранее знать длину списка, то заголовок может выглядеть так: for i in range(4). То, как используется i в теле цикла, вопрос второй. Примечание. Вместо идентификатора i может быть любой другой.

Функция enumerate()

В Python есть еще одна встроенная функция, которая часто используется в заголовке for. Это функция enumerate(). Если range() позволяет получить только индексы элементов списка, то enumerate() генерирует пары кортежей, состоящих из индекса элемента и значения элемента.

>>> spisok = [16, 46, 26, 36]
>>> for i in enumerate(spisok):
...     print(i)
... 
(0, 16)
(1, 46)
(2, 26)
(3, 36)

Эти кортежи можно распаковывать, то есть извлекать индекс и значение, в теле цикла:

>>> for item in enumerate(spisok):
...     print(item[0], item[1])
... 
0 16
1 46
2 26
3 36

Однако чаще это делают еще в заголовке for, используя две переменные перед in:

>>> for id, val in enumerate(spisok):
...     print(id, val)
... 
0 16
1 46
2 26
3 36

Если функция enumerate() так хороша, зачем использовать range() в заголовке for? На самом деле незачем, если только вам так не проще. Кроме того, бывают ситуации, когда значения не нужны, нужны только индексы. Однако следует учитывать один нюанс. Функция enumerate() возвращает так называемый объект-итератор. Когда такие объекты сгенерировали значения, то становятся "пустыми". Второй раз по ним пройтись нельзя.

Функция range() возвращает итерируемый объект. Хотя такой объект может быть превращен в итератор, сам по себе таковым не является.

Когда range() и enumerate() используются в заголовке for, то разницы нет, так как range- и enumerate-объекты не присваиваются переменным и после завершения работы цикла теряются. Но если мы присваиваем эти объекты переменным, разница есть:

>>> r_obj = range(len(spisok))
>>> e_obj = enumerate(spisok)
>>> for i in r_obj:
...     if i == 1:
...             break
... 
>>> for i in e_obj:
...     if i[0] == 1:
...             break
... 
>>> for i in r_obj:
...     print(i)
... 
0
1
2
3
>>> for i in e_obj:
...     print(i)
... 
(2, 26)
(3, 36)

Сначала мы прерываем извлечение элементов из объектов на элементе с индексом 1. Когда снова прогоняем объекты через цикл for, то в случае r_obj обход начинается сначала, а в случае e_obj продолжается после места останова. Объект e_obj уже не содержит извлеченных ранее элементов.

Практическая работа

  1. Заполните список случайными числами. Используйте в коде цикл for, функции range() и randint().

  2. Если объект range (диапазон) передать встроенной в Python функции list(), то она преобразует его к списку. Создайте таким образом список с элементами от 0 до 100 и шагом 17.

  3. В заданном списке, состоящем из положительных и отрицательных чисел, посчитайте количество отрицательных элементов. Выведите результат на экран.

  4. Напишите программу, которая заполняет список пятью словами, введенными с клавиатуры, измеряет длину каждого слова и добавляет полученное значение в другой список. Например, список слов – ['yes', 'no', 'maybe', 'ok', 'what'], список длин – [3, 2, 5, 2, 4]. Оба списка должны выводиться на экран.

Примеры решения и дополнительные уроки в android-приложении и pdf-версии курса.

Комментарии

>>> i = 0
Ошибка в выводе
>>> while i < len(spisok):
...     spisok[i] = spisok[i] + 2 # или spisok += 2
...     i = i + 1 # или i += 1
... 
>>> spisok
[14, 44, 24, 34] 
вывод [12, 42, 22, 32]

Ответ на от wittmann404

Никогда! Слышите?! Никогда! Так не пишите!
for i in range(len(spisok)):
За такое сразу руки отрывать нужно.

Ответ на от миха

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

>>> a = [1, 2, 3, 4]
>>> for i in range(len(a)):
...     a[i] += 10
... 
>>> a
[11, 12, 13, 14]

from random import randint
#1 task
a = []
 
for i in range(0, 9):
    a.append(randint(-300, 300))
 
print ('Task1:', a)
 
 
#2 task
c = list(range(0,100,17))
print ('Task2:', c)
 
 
#3 task. Как понял нужно отнести к списку из 1-го задания
negative = []
 
for i in a:
    if i < 0:
        negative.append(i)
 
print('Task3:Negative:', negative)
print('The number of negative:' , len(negative))

#4Task
 
cir = 0
a = []
b = []
 
for items in range(0,5):
    words = input('Enter some words:')
    b.append(len(words))
    a.append(words)
    cir = cir+1
 
print (a)
print(b)

l = []
num = 1
for i in range(5):
    l.append(input("Enter string №{0} ".format(num)))
    num += 1
 
nl = []
for i in l:
    nl.append(len(i))
 
print(l)
print(nl)