Перейти к содержанию

412 уроков, 25 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 2 из 20 Начальный 15 мин 100 XP

range в Python: цикл со счётчиком

range делает цикл for счётчиком: n повторов от нуля, диапазон от начала до конца, шаг и обратный отсчёт. Разбираем все три формы range на примерах с запуском.

Редакция Питоники

Цикл из первого урока ходил по готовым данным: список гостей, список шагов. Но половина повторов в программах — не данные, а счёт: сделать 5 повторов, пронумеровать страницы от 1 до 10, посчитать секунды до запуска. Создавать список чисел руками ради цикла странно. Для этого в Python есть range — встроенный генератор числовых последовательностей, главный способ управлять счётчиком цикла for.

range(n): n шагов от нуля

Простейшая форма — один аргумент: range(n) даёт циклу n чисел, начиная с нуля:

пять шагов
for i in range(5):
    print(i)
Вывод
0
1
2
3
4

Пять чисел — от 0 до 4. Пятёрки в переборе нет: range(5) — это «пять шагов, нумерация с нуля», а не «числа до пяти». range(n) делает ровно n шагов: числа идут от 0 до n-1, а само число n не входит. Такое соглашение выглядит непривычно, но у него есть скрытая аккуратность: сколько чисел заказали, столько шагов и получили — без единицы сверху. В переменную i на каждом шаге попадает очередной номер: 0, 1, 2 и так далее.

повторить действие три раза
for i in range(3):
    print(f'Повтор номер {i}')
Вывод
Повтор номер 0
Повтор номер 1
Повтор номер 2

range — это не список

Первое, что стоит понять про range: это не список чисел, а описание диапазона. Python не создаёт миллион элементов, чтобы выдать вам числа от нуля до миллиона. Диапазон хранит всего три вещи: с чего начинать, где заканчивать и с каким шагом. Числа рождаются в момент, когда цикл просит следующий.

миллион шагов без миллиона элементов
big = range(1000000)
print(big)
print(len(list(range(1000000))))
Вывод
range(0, 1000000)
1000000

Первая строка показывает, что range(1000000) — это диапазон, а не список: в скобках видны границы, а не миллион чисел. Вторая строка отвечает на практический вопрос «а сколько там шагов» — миллион. Но чтобы их увидеть, пришлось превратить диапазон в список, и вот тут память уже тратится по-настоящему.

range(начало, конец)

Два аргумента задают, с какого числа начать и каким закончить: range(начало, конец). Левая граница входит в перебор, правая — нет:

числа от 1 до 10
for day in range(1, 11):
    print(f'День {day}')
Вывод
День 1
День 2
День 3
День 4
День 5
День 6
День 7
День 8
День 9
День 10

Чтобы получить «от 1 до 10», правую границу пишут на единицу больше — 11. Это то же правило, что у срезов строк: s[1:11] тоже берёт символы с 1-го по 10-й — правило «левая включена, правая нет» в Python одно на всё. Промежуток без начала тоже валиден: range(5, 10) — числа 5, 6, 7, 8, 9. А запись range(5, 1) с началом больше конца даст пустой набор — цикл не сделает ни одного шага, разберём ниже.

ЗаписьЧислаСколько шагов
range(5)0, 1, 2, 3, 45
range(1, 6)1, 2, 3, 4, 55
range(2, 8)2, 3, 4, 5, 6, 76
range(5, 1)ничего0

Третий аргумент: шаг

Третий аргумент говорит, на сколько прыгать между числами. range(2, 11, 2) идёт от 2 до 10 с шагом 2 — только чётные:

шаг два
for num in range(2, 11, 2):
    print(num)
Вывод
2
4
6
8
10

Шаг делает перебор экономным: не «все числа подряд», а каждое второе, каждое пятое. Последний прыжок может перескочить правую границу — тогда она просто не попадёт в перебор: range(1, 10, 3) даст 1, 4, 7 — а попытка прыгнуть с 7 на 10 уже не сработает, потому что 10 не входит.

Сколько шагов в диапазоне

Частый вопрос новичка: «а сколько раз выполнится тело?» Ответ считается на пальцах, но лучше один раз проверить руками. Длина диапазона — это просто разница границ, делённая на шаг: у range(1, 11) шагов десять, у range(2, 11, 2) — пять, у обратного range(10, 0, -1) — тоже десять.

длины диапазонов
print(len(range(5)))
print(len(range(1, 11)))
print(len(range(2, 11, 2)))
print(len(range(10, 0, -1)))
Вывод
5
10
5
10

Функция len работает с диапазоном и не создаёт его в память: она просто считает шаги по правилам. Если вам нужно знать размер заранее — например, чтобы проверить, не обрежется ли что-то в конце, — считайте через len, а не гадайте.

Шаг не обязан попадать на границу

Границы диапазона и шаг не обязаны дружить. Если шаг не попадает ровно на правую границу, последний прыжок перескакивает её — и просто не попадает в перебор. Ничего не ломается, но стоит знать, что происходит:

шаг три до десяти
for n in range(0, 10, 3):
    print(n)
Вывод
0
3
6
9

Дальше был бы прыжок с 9 на 12 — и 12 уже за правой границей, поэтому в перебор не попал. Отсюда практическое правило: если нужно «каждый третий до десяти включительно», границу пишут на единицу больше нужного: range(0, 11, 3) даст 0, 3, 6, 9 — а вот 12 не входит, потому что граница 11.

Обратный отсчёт

Отрицательный шаг переворачивает перебор: числа идут вниз, от начала к концу. Классика жанра — запуск ракеты:

обратный отсчёт
for sec in range(3, 0, -1):
    print(f'{sec}...')
print('Пуск!')
Вывод
3...
2...
1...
Пуск!

range(3, 0, -1) даёт 3, 2, 1 — и останавливается, не дойдя до нуля: правая граница не включается и при отрицательном шаге тоже. Если в отсчёте нужен сам ноль, границу пишут на единицу «ниже»: range(3, -1, -1) даст 3, 2, 1, 0:

отсчёт с нулём на конце
for n in range(3, -1, -1):
    print(n)
Вывод
3
2
1
0
цикл, который не сделает ни шага
for n in range(5, 1):
    print(n)
print('Тело не выполнилось ни разу')
Вывод
Тело не выполнилось ни разу
шаг ноль — ошибка
for n in range(1, 10, 0):
    print(n)
Не запускается: шаг 0 Python запрещает — будет ValueError: range() arg 3 must not be zero. Ноль шаг между числами не осмыслен: следующего числа не существует.

Диапазон нельзя изменить

Список можно пополнить методом append, а диапазон — нет. Это не недоработка, а сознательное решение: range описывает правило, а правило нельзя дополнить. Если во время цикла понадобится положить в него ещё одно число, придётся сначала превратить диапазон в список — и тогда про экономию памяти можно забыть.

append у диапазона
r = range(5)
r.append(10)
Не запускается: AttributeError: 'range' object has no attribute 'append'. Диапазон неизменяем — как строка или кортеж. Нужен изменяемый список: r = list(range(5)), и тогда append заработает.

Практика: рабочая неделя

Соберём вместе всё, что разобрали: диапазон дней, условие внутри тела, две ветки. Задача простая — отметить будни и выходные. Читается как готовая формула: диапазон даёт дни, условие делит их на две группы.

будни и выходные
for day in range(1, 8):
    if day % 2 == 0:
        print(day, '- рабочий день')
    else:
        print(day, '- выходной')
Вывод
1 - выходной
2 - рабочий день
3 - выходной
4 - рабочий день
5 - выходной
6 - рабочий день
7 - выходной

Остаток от деления % — рабочая лошадка в задачах с чётностью, и внутри цикла он работает ровно так же, как вне его. Обратите внимание на границу: диапазон задан как range(1, 8), поэтому дни идут с первого по седьмой — седьмой день уже входит, а вот range(1, 7) закончил бы перебор на шестом.

Когда range, а когда список

Правило выбора сводится к вопросу «что перебираем». Есть данные, которые нужно обработать, — цикл ходит по самим данным: по списку гостей, по строке, по словарю. Данных нет, а нужны повторы или номера, — берётся range: пять повторов, страницы от 1 до 10, обратный отсчёт. Соединять обе стороны умеет enumerate: он отдаёт и номер, и элемент сразу — урок 4 разбирает его целиком. А если совсем без данных, но с текстом на каждый шаг — диапазон печатает счёт прямо в теле, как в примерах выше.

  • range(n) — повторить действие n раз: нумерация с нуля, n шагов;
  • range(начало, конец) — перебор по отрезку: левая граница входит, правая нет — как и у срезов;
  • range(начало, конец, шаг) — прыжки через несколько: чётные, каждое пятое;
  • отрицательный шаг — обратный отсчёт: range(10, 0, -1) идёт от 10 до 1.

range закрывает вопрос «сколько шагов». Но данные, по которым ходит for, — не только списки и диапазоны. Строка тоже последовательность, словарь перебирается по ключам, а файл — по строкам. Следующий урок расширит понятие «по чему можно идти циклом».

Что выведет код?

Сначала предскажи ответ в голове — это главный навык программиста.

for i in range(3):
    print(i)
for n in range(3, 6):
    print(n)
for n in range(6, 0, -2):
    print(n)
Проверь себя
0 / 9

1. Какие числа перебирает range(4)?

2. Сколько шагов сделает цикл for i in range(2, 8)?

3. Что делает третий аргумент range(0, 21, 5)?

4. Как написать обратный отсчёт от 5 до 1?

5. Что произойдёт с циклом for n in range(7, 3)?

6. Что напечатает range(1, 11) на последнем шаге?

7. Что произойдёт, если попытаться дописать в range(5) число 10?

8. Сколько чисел даст range(0, 10, 3)?

9. Чем range отличается от списка чисел?

Карточки терминов
Запомнено: 0 / 9
Практика

Сделай обратный отсчёт от 5 до 1: печатай каждое число на своей строке, а после цикла — строку «Старт!».

practice.py
Вопросы и ответы по уроку

Что делает range в Python?

range даёт последовательность чисел для перебора: range(5) — пять чисел от 0 до 4. Он не список, а «ленивый» счётчик, но в цикле for ведёт себя как список чисел: for i in range(5): print(i) напечатает числа от 0 до 4.

Урок 1: первый цикл for

Как перебрать числа от 1 до 10?

range(1, 11): левая граница входит в перебор, правая нет, поэтому конец пишут на единицу больше нужного числа. Тот же принцип у срезов строк — правило «левая включена, правая нет» в Python единое.

Почему range(5) не доходит до 5?

Потому что range(n) обещает ровно n шагов: числа 0, 1, 2, 3, 4 — их пять. Нумерация в Python начинается с нуля, и правая граница всегда не включается. Если нужно включить n, пишут range(1, n + 1) или range(n + 1).

Как сделать обратный отсчёт в цикле?

Отрицательный шаг: range(10, 0, -1) перебирает числа 10, 9, ..., 1. Ноль в этот перебор не попадает; если нужен ноль на конце, границу опускают до -1: range(10, -1, -1).

Можно ли изменить range во время цикла?

Нет: диапазон неизменяем, и методов append или insert у него нет — Python ответит AttributeError. Если результаты нужно копить, заводите отдельный список и складывайте туда, а range оставьте генератором шагов.

Почему range(1, 11) даёт десять чисел, а не одиннадцать?

Правая граница не включается: перебор идёт по числам 1, 2, ..., 10 и останавливается перед 11. Чтобы последним было число N, границу пишут N + 1 — range(1, N + 1).

Понравился урок? Сошлитесь на него

«range(n) делает ровно n шагов: числа идут от 0 до n-1, а само число n не входит.»

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Что дальше

Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.