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

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

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

Счётчики и аккумуляторы в циклах Python

Сумма чека, число совпадений, лучший результат: переменная, объявленная до цикла и копящая значения внутри, — приём старше языков. Разбираем счётчики, суммы, максимумы и их ловушки.

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

Посчитать сумму покупок, число совпадений, лучший результат — задачи, в которых цикл не просто перебирает, а накапливает. Для этого у программистов есть приём старше языков высокого уровня: переменная-аккумулятор. Объявили до цикла, копили внутри, прочитали после — разберём суммы, счётчики, максимумы и главные ловушки каждого.

Аккумулятор: сумма

Чек из четырёх покупок — сложим все цены через цикл:

сумма через total
prices = [89, 42, 590, 60]

total = 0
for p in prices:
    total += p
print(total)
Вывод
781

total объявлен до цикла со стартовым нулём. На каждой итерации total += p прибавляет текущую цену к накопленному; запись total += p — короткая форма total = total + p. К выходу из цикла в total лежит сумма: 781. Место аккумулятора запомните раз и навсегда: старт — до цикла, накопление — внутри, чтение — после.

Счётчик: считаем события

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

счётчик жарких дней
temps = [18, 25, 30, 11, 27]

hot = 0
for t in temps:
    if t >= 25:
        hot += 1
print(hot)
Вывод
3

Счётчик — тот же аккумулятор, только шаг всегда единица. hot растёт не на значение элемента, а на сам факт: температура попала в порог. Схема совпадает до буквы: объявили с нулём до цикла, увеличили внутри, прочитали после.

Фильтр перед накоплением — частый гость: посчитаем сумму только по недорогим позициям.

сумма со шлагбаумом
prices = [89, 42, 590, 60]

total = 0
for p in prices:
    if p >= 100:
        continue
    total += p
print(total)
Вывод
191

Дорогая позиция отсеивается continue — и в сумму попадает только дешёвое: 89 + 42 + 60. Условие перед накоплением — самый короткий путь от простого подсчёта к подсчёту с правилами.

Два аккумулятора за один проход

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

среднее за один проход
temps = [18, 25, 30, 11, 27]
total = 0
count = 0
for t in temps:
    total = total + t
    count = count + 1
print('среднее:', total / count)
Вывод
среднее: 22.2

22.2 — это 111, делённое на 5. Оба аккумулятора объявлены до цикла и растут внутри. Если бы мы считали сумму одним циклом, а количество через len, результат совпал бы, но список пришлось бы перебирать дважды. На больших данных это удвоенное время, на маленьких — пара лишних строк.

Максимум и минимум вручную

Аккумулятор умеет копить не только суммы. Найдём самый жаркий день:

максимум через чемпиона
temps = [18, 25, 30, 11, 27]

best = temps[0]
for t in temps:
    if t > best:
        best = t
print(best)
Вывод
30

Стартуем с первого элемента: best = temps[0]. Дальше каждый претендент сравнивается с чемпионом — кто больше, тот и новый чемпион. К концу перебора в best лежит максимум.

А вот что будет, если стартовать с нуля — на первый взгляд тоже сойдёт:

максимум на отрицательных числах
lows = [-12, -5, -30]

best = 0
for t in lows:
    if t > best:
        best = t
print(best)
Вывод
0

Напечатан ноль — числа из списка его не перебили: ни одно не оказалось больше нуля, чемпион остался фантомом. Правильный ответ -5 так и не выбрался из списка.

Максимум и минимум за один проход

Иногда нужны оба экстремума сразу, а данных — один и тот же список. Два отдельных цикла работают, но это два прохода по одним и тем же числам. Проще объявить двух кандидатов и сравнивать с обоими за один шаг.

оба экстремума за один проход
temps = [18, 25, 30, 11, 27]
best = temps[0]
worst = temps[0]
for t in temps:
    if t > best:
        best = t
    if t < worst:
        worst = t
print(best, worst)
Вывод
30 11

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

Ловушка обнуления

Вторая классическая ошибка — объявить аккумулятор внутри цикла. Код выглядит почти правильно:

обнуление внутри тела
prices = [89, 42, 590]
for p in prices:
    total = 0
    total += p
print(total)
Вывод
590

Напечатана 590 — только последняя цена. Обнуление внутри тела стирает накопленное на каждой итерации: к концу цикла в total лежит лишь вклад последнего шага. Вся сумма 721 погибла по дороге. Лечится переносом одной строки — total = 0 должен встать до for.

Словарь-аккумулятор

Аккумулятором может быть не только число. Словарь, в котором ключ — это категория, а значение — счётчик, умеет считать группы за один проход. Это ровно то, что делает подсчёт слов в тексте, заказов по товарам или ошибок по кодам.

считаем, сколько раз встретилось каждое слово
words = ['кот', 'ёж', 'кот', 'пёс', 'кот']
counter = {}
for w in words:
    if w in counter:
        counter[w] = counter[w] + 1
    else:
        counter[w] = 1
print(counter)
Вывод
{'кот': 3, 'ёж': 1, 'пёс': 1}

Разбор логики: если ключа ещё нет — создаём его со значением 1, если есть — увеличиваем на единицу. Словарь не пришлось объявлять заранее: первый же прошедший элемент создаст свой ключ. Порядок ключей в выводе — порядок первого появления, а не алфавитный.

Счётчик удобно сочетать с continue — так за один проход считают сразу две разные величины:

два счётчика за один проход
grades = [5, 3, 5, 2, 5, 4]
fives = 0
twos = 0
for g in grades:
    if g == 5:
        fives = fives + 1
        continue
    if g == 2:
        twos = twos + 1
print('пятёрок:', fives, 'двоек:', twos)
Вывод
пятёрок: 3 двоек: 1

Три пятёрки и одна двойка — цикл прошёл все шесть оценок. Continue здесь работает как «дальше не смотри»: после подсчёта пятёрки цикл сразу переходит к следующей оценке, не проверяя её на двойку. Разделение на два счётчика читается легче, чем одно условие с двумя проверками внутри.

Готовые инструменты: sum, max, min

Простые накопления встроены в язык — и под капотом у них тот же цикл с аккумулятором:

встроенные суммы и экстремумы
temps = [18, 25, 30, 11, 27]
print(sum(temps))
print(max(temps), min(temps))
Вывод
111
30 11

sum складывает, max и min находят экстремумы — три строки вместо трёх циклов. Руками аккумуляторы пишут, когда нужно больше: фильтр перед накоплением, несколько величин за один проход, свой критерий сравнения. Среднее, например, считают двумя аккумуляторами сразу: сумма плюс количество, потом деление.

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

сумма чека с вводом
total = 0
while True:
    price = input('Цена (пустая строка — конец): ')
    if price == '':
        break
    total += int(price)
print('Итого:', total)
Блок с input() в песочнице не запустится — ввода там нет. Локально он собирает сумму, пока не нажать Enter на пустой строке.

Здесь аккумулятор копит сумму, а пустая строка работает стоп-сигналом — два приёма этого урока в одной программе. Подробно про break и while True — в тринадцатом уроке, а поведение while с условием — в пятом.

ЗадачаСтартШаг внутри цикла
Суммаtotal = 0total += x
Счётчик событийcount = 0count += 1 внутри if
Максимумbest = первый элементif x > best: best = x
Минимумworst = первый элементif x < worst: worst = x
Что выведет код?

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

total = 0
for n in [5, 10, 15]:
    total += n
print(total)
count = 0
for w in ['кот', 'барсук', 'уж']:
    if len(w) <= 3:
        count += 1
print(count)
total = 0
for p in [50, 25]:
    total = 1
    total += p
print(total)
Проверь себя
0 / 8

1. Где объявляют аккумулятор?

2. Чему равносильно total += x?

3. С чего правильно начинать поиск максимума в списке?

4. Чем счётчик отличается от суммы-аккумулятора?

5. Что вернёт sum([2, 3, 4])?

6. Как посчитать среднее арифметическое за один проход по списку?

7. С чего правильно начинать поиск и максимума, и минимума?

8. Что делает словарь-аккумулятор при подсчёте частот?

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

Для списка оценок grades = [4, 5, 3, 5, 4] посчитайте за один проход сумму через аккумулятор total и количество пятёрок через счётчик fives. Выведите обе величины одной строкой: print(total, fives).

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

Как посчитать сумму чисел в цикле Python?

Объявить total = 0 до цикла и внутри делать total += x; после цикла в total — сумма. Для простого случая без фильтров короче встроенная sum(list) — тот же цикл, свёрнутый в один вызов.

Урок 2: range

Почему максимум нельзя искать от нуля?

На списке отрицательных чисел ни один элемент не превзойдёт ноль, и цикл вернёт фантомный ноль вместо настоящего максимума. Правильный старт — первый элемент списка: он гарантирует, что чемпион — настоящее число из данных.

Зачем писать аккумулятор руками, если есть sum()?

Ручной цикл нужен, когда условий больше: отобрать перед накоплением (continue или if), посчитать несколько величин за один проход, найти максимум по своему правилу. sum, max и min закрывают базовые случаи одной строкой.

Как посчитать среднее арифметическое в цикле?

Двумя аккумуляторами за один проход: total += x и count += 1, после цикла total / count. Так не приходится дважды перебирать список — сумма и количество копятся параллельно. Пустой список проверьте заранее: деление на ноль никуда не денется.

Как посчитать, сколько раз встретилось каждое слово?

Словарём-аккумулятором: если ключа ещё нет — создаём его со значением 1, если есть — увеличиваем на единицу. Готовый аналог — collections.Counter из стандартной библиотеки.

Урок 3: for по словарю

Почему average считают двумя аккумуляторами, а не через sum и len?

Чтобы перебрать данные один раз: total и count растут параллельно. Вариант sum(temps) / len(temps) даёт тот же ответ, но заставляет список обходить дважды. На больших данных разница в скорости заметна.

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

«Счётчик — тот же аккумулятор, только шаг всегда единица.»

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

TelegramVK

Что дальше

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

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

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