💻
Разработка в IT
Опубликовано:
16.04.2026
Обновлено:
16.04.2026

Шаблоны циклов в Python: подсчёт, сумма, максимум, минимум

Анна Николаева

Когда я только осваивал Python, задача "найти максимум в списке" казалась тривиальной, пока не пришлось обрабатывать данные без встроенных функций. Написав пять циклов за час, я заметил, что каждый из них следует одной и той же структуре: переменная-аккумулятор перед циклом, обновление на каждой итерации, результат после цикла. Это и есть шаблоны циклов (loop patterns): повторяющиеся конструкции для подсчёта, суммирования, поиска максимума и минимума.

Статья разбирает четыре базовых шаблона, показывает их реализацию вручную и через встроенные функции Python, сравнивает подходы и объясняет, как комбинировать шаблоны в одном цикле.

Что такое шаблон цикла и зачем он нужен

Шаблон цикла (loop pattern) это повторяющаяся структура кода, где переменная-аккумулятор накапливает результат по мере прохождения элементов коллекции. Каждый шаблон состоит из трёх частей: инициализация аккумулятора до цикла, обновление аккумулятора внутри цикла, использование результата после цикла.

Шаблоны полезны по двум причинам. Первая: они превращают задачу обработки данных в предсказуемый алгоритм. Вторая: понимание шаблонов помогает выбрать правильную встроенную функцию Python, потому что len(), sum(), max() и min() реализуют те же самые паттерны внутри.

Общая структура

# 1. Инициализация аккумулятора
accumulator = начальное_значение

# 2. Цикл с обновлением
for element in collection:
    accumulator = обновление(accumulator, element)

# 3. Результат
print(accumulator)

Меняется только начальное значение и логика обновления. Всё остальное одинаково для подсчёта, суммирования, максимума и минимума.

Шаблон подсчёта (counting)

Подсчёт отвечает на вопрос "сколько элементов в коллекции?" или "сколько элементов удовлетворяют условию?". Аккумулятор инициализируется нулём и увеличивается на 1 при каждой итерации (или при выполнении условия).

Подсчёт всех элементов

count = 0
for itervar in [3, 41, 12, 9, 74, 15]:
    count = count + 1
print("Количество:", count)
# Количество: 6

Переменная count начинается с 0. На каждой итерации значение itervar не используется, count просто растёт на 1. После цикла count содержит число элементов.

Подсчёт по условию

Задача: посчитать, сколько чисел в списке больше 10.

numbers = [3, 41, 12, 9, 74, 15]
count = 0
for num in numbers:
    if num > 10:
        count += 1
print("Больше 10:", count)
# Больше 10: 4

Здесь count увеличивается не на каждой итерации, а только когда num > 10. Это расширение базового шаблона условным оператором.

Подсчёт символов в строке

word = "banana"
count = 0
for letter in word:
    if letter == "a":
        count += 1
print(count)
# 3

Строка в Python итерируема, поэтому for перебирает её посимвольно. Шаблон тот же: инициализация 0, условие, инкремент.

Замена встроенной функцией

numbers = [3, 41, 12, 9, 74, 15]
print(len(numbers))  # 6

# Подсчёт по условию через генераторное выражение
print(sum(1 for num in numbers if num > 10))  # 4

Функция len() заменяет простой подсчёт. Для подсчёта по условию удобна конструкция sum(1 for ...), где генератор выдаёт 1 для каждого подходящего элемента.

Шаблон суммирования (summing)

Суммирование отвечает на вопрос "какова общая сумма элементов?". Аккумулятор инициализируется нулём и на каждой итерации увеличивается на значение текущего элемента.

Базовое суммирование

total = 0
for itervar in [3, 41, 12, 9, 74, 15]:
    total = total + itervar
print("Сумма:", total)
# Сумма: 154

Переменная total начинается с 0. На каждой итерации к ней прибавляется itervar. Пошаговое выполнение:

total = 0 + 3 = 3
total = 3 + 41 = 44
total = 44 + 12 = 56
total = 56 + 9 = 65
total = 65 + 74 = 139
total = 139 + 15 = 154

Суммирование с условием

Задача: сложить только положительные числа.

data = [10, -3, 25, -7, 8, 0, -1]
positive_sum = 0
for num in data:
    if num > 0:
        positive_sum += num
print("Сумма положительных:", positive_sum)
# Сумма положительных: 43

Среднее арифметическое: комбинация подсчёта и суммирования

numbers = [3, 41, 12, 9, 74, 15]
count = 0
total = 0
for num in numbers:
    count += 1
    total += num
average = total / count
print("Среднее:", average)
# Среднее: 25.666666666666668

Два аккумулятора работают в одном цикле. count считает элементы, total складывает значения. После цикла делим одно на другое.

Замена встроенной функцией

numbers = [3, 41, 12, 9, 74, 15]
print(sum(numbers))  # 154
print(sum(numbers) / len(numbers))  # 25.666...

Функция sum() принимает итерируемый объект и необязательный аргумент start (по умолчанию 0).

Шаблон поиска максимума

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

Базовый поиск максимума

largest = None
print("До цикла:", largest)
for itervar in [3, 41, 12, 9, 74, 15]:
    if largest is None or itervar > largest:
        largest = itervar
    print("Итерация:", itervar, largest)
print("Максимум:", largest)

Вывод:

До цикла: None
Итерация: 3 3
Итерация: 41 41
Итерация: 12 41
Итерация: 9 41
Итерация: 74 74
Итерация: 15 74
Максимум: 74

На первой итерации largest равен None, поэтому условие largest is None истинно, и largest получает значение 3. На второй итерации 41 > 3, поэтому largest обновляется до 41. Значение 12 меньше 41, обновления нет. На пятой итерации 74 > 41, largest становится 74.

Почему None, а не первый элемент

Инициализация через None безопаснее, чем присваивание первого элемента вручную. Если список пуст, largest остаётся None, и программа не упадёт с ошибкой IndexError. Условие largest is None работает как "страховка" для первой итерации.

Альтернативный вариант с первым элементом:

numbers = [3, 41, 12, 9, 74, 15]
largest = numbers[0]  # упадёт, если список пуст
for num in numbers[1:]:
    if num > largest:
        largest = num
print(largest)  # 74

Этот вариант создаёт срез numbers[1:], который копирует часть списка в память. Для больших списков это расточительно.

Замена встроенной функцией

numbers = [3, 41, 12, 9, 74, 15]
print(max(numbers))  # 74

# max с пустым списком
print(max([], default=None))  # None (без default будет ValueError)

Функция max() принимает аргумент default (Python 3.4+), который возвращается при пустом итерируемом объекте.

Шаблон поиска минимума

Поиск минимума зеркален поиску максимума. Единственное отличие: условие обновления меняется с > на <.

Базовый поиск минимума

smallest = None
for itervar in [3, 41, 12, 9, 74, 15]:
    if smallest is None or itervar < smallest:
        smallest = itervar
print("Минимум:", smallest)
# Минимум: 3

Реализация в виде функции

def find_min(values):
    smallest = None
    for value in values:
        if smallest is None or value < smallest:
            smallest = value
    return smallest


print(find_min([3, 41, 12, 9, 74, 15]))  # 3
print(find_min([]))  # None

Эта функция повторяет логику встроенной min() в упрощённом виде.

Сравнение четырёх шаблонов

Шаблон Начальное значение Обновление Встроенная функция
Подсчёт count = 0 count += 1 len()
Суммирование total = 0 total += element sum()
Максимум largest = None if el > largest: largest = el max()
Минимум smallest = None if el < smallest: smallest = el min()

Подсчёт и суммирование инициализируются нулём, потому что 0 нейтрален для сложения. Максимум и минимум инициализируются None, потому что любое заранее выбранное число может оказаться больше или меньше реального экстремума.

Комбинирование шаблонов в одном цикле

Несколько аккумуляторов могут работать одновременно. Это экономит время: один проход по данным вместо четырёх.

numbers = [3, 41, 12, 9, 74, 15]

count = 0
total = 0
largest = None
smallest = None

for num in numbers:
    count += 1
    total += num
    if largest is None or num > largest:
        largest = num
    if smallest is None or num < smallest:
        smallest = num

print(f"Количество: {count}")
print(f"Сумма: {total}")
print(f"Среднее: {total / count}")
print(f"Максимум: {largest}")
print(f"Минимум: {smallest}")

Вывод:

Количество: 6
Сумма: 154
Среднее: 25.666666666666668
Максимум: 74
Минимум: 3

Когда комбинировать, а когда нет

Комбинирование полезно, когда данные читаются из файла или потока, и повторный проход невозможен или дорог. Если данные уже в списке, вызовы len(), sum(), max(), min() нагляднее и написаны на C, то есть работают быстрее цикла на Python.

Практический пример: статистика оценок студентов

Задача: прочитать оценки от пользователя, вычислить количество, сумму, среднее, максимальную и минимальную оценку.

count = 0
total = 0
largest = None
smallest = None

while True:
    line = input("Введите оценку (done для завершения): ")
    if line == "done":
        break
    try:
        score = float(line)
    except ValueError:
        print("Некорректный ввод, попробуйте снова")
        continue

    count += 1
    total += score
    if largest is None or score > largest:
        largest = score
    if smallest is None or score < smallest:
        smallest = score

if count > 0:
    print(f"Оценок: {count}")
    print(f"Сумма: {total}")
    print(f"Среднее: {total / count:.2f}")
    print(f"Максимум: {largest}")
    print(f"Минимум: {smallest}")
else:
    print("Оценки не введены")

Здесь объединены шаблон while True с break, обработка ошибок через try/except с continue и все четыре аккумулятора. Проверка count > 0 защищает от деления на ноль.

Шаблоны за пределами чисел: строки и словари

Аккумуляторный паттерн применим не только к числам.

Конкатенация строк

words = ["Python", "это", "просто"]
result = ""
for word in words:
    result = result + word + " "
print(result.strip())
# Python это просто

Аккумулятор result инициализируется пустой строкой. На каждой итерации к нему прибавляется слово с пробелом. Питоничнее использовать " ".join(words), но паттерн тот же.

Подсчёт частоты символов (гистограмма)

word = "brontosaurus"
counts = {}
for char in word:
    counts[char] = counts.get(char, 0) + 1
print(counts)
# {'b': 1, 'r': 2, 'o': 2, 'n': 1, 't': 1, 's': 2, 'a': 1, 'u': 2}

Аккумулятор здесь словарь. Метод dict.get(key, default) возвращает текущее значение или 0, если ключ отсутствует. Это шаблон подсчёта с группировкой по ключу.

Сбор элементов по условию (фильтрация в список)

numbers = [3, 41, 12, 9, 74, 15]
big = []
for num in numbers:
    if num > 20:
        big.append(num)
print(big)  # [41, 74]

Аккумулятор big это пустой список. Шаблон подсчёта заменён на шаблон накопления: вместо += 1 используется append().

Ручной цикл vs встроенные функции: что выбрать

Критерий Ручной цикл Встроенные функции
Читаемость Средняя (5-10 строк) Высокая (1 строка)
Производительность Медленнее (интерпретируемый Python) Быстрее (реализованы на C)
Гибкость Полная (любая логика) Ограниченная (фиксированное поведение)
Несколько агрегаций за один проход Да Нет (отдельный вызов для каждой)
Потоковые данные (файл, сеть) Да Зависит от реализации

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

Неочевидные детали

Первый факт: sum() принимает второй аргумент start. По умолчанию start=0, но можно передать другое значение. Например, sum([1, 2, 3], 10) вернёт 16.

Второй факт: max() и min() принимают аргумент key, который задаёт функцию сравнения. Например, max(words, key=len) вернёт самое длинное слово. Ручной цикл для этого потребует двух аккумуляторов: один для максимальной длины, второй для самого слова.

words = ["cat", "elephant", "dog", "hippopotamus"]
print(max(words, key=len))  # hippopotamus
print(min(words, key=len))  # cat

Третий факт: для вычисления произведения в Python 3.8+ появилась math.prod():

import math

numbers = [2, 3, 4, 5]
print(math.prod(numbers))  # 120

До Python 3.8 использовали functools.reduce():

from functools import reduce
from operator import mul

print(reduce(mul, [2, 3, 4, 5]))  # 120

Четвёртый факт: collections.Counter реализует шаблон подсчёта частоты в одну строку:

from collections import Counter

word = "brontosaurus"
print(Counter(word))
# Counter({'r': 2, 'o': 2, 's': 2, 'u': 2, 'b': 1, 'n': 1, 't': 1, 'a': 1})

FAQ

Почему максимум и минимум инициализируют через None, а не через float('inf')?

Оба варианта рабочие. None безопаснее, потому что подходит для любого типа данных: строки, кортежи, пользовательские объекты. Значение float('inf') работает только с числами. При инициализации через None первый элемент всегда записывается в аккумулятор.

Можно ли использовать enumerate() в шаблонах подсчёта?

Да. Функция enumerate() возвращает пары (индекс, элемент). Но для простого подсчёта проще использовать len(). enumerate() полезен, когда нужен и индекс, и значение одновременно.

Как найти индекс максимального элемента?

Через ручной цикл или встроенные функции:

numbers = [3, 41, 12, 74, 15]

# Вариант 1: ручной цикл
max_idx = 0
for i in range(1, len(numbers)):
    if numbers[i] > numbers[max_idx]:
        max_idx = i
print(max_idx)  # 3

# Вариант 2: встроенные функции
print(numbers.index(max(numbers)))  # 3

Работают ли шаблоны с генераторами?

Да. Функции sum(), max(), min() принимают генераторные выражения. Генератор не создаёт список в памяти, а выдаёт элементы по одному:

print(sum(x**2 for x in range(1000)))

Зачем писать шаблоны вручную, если есть встроенные функции?

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

Мой совет: запомните структуру "инициализация, цикл, результат" и научитесь определять, какой тип обновления нужен. Дальше выбор между ручным циклом и встроенной функцией станет очевидным: одна агрегация по готовому списку это len/sum/max/min, всё остальное это цикл с аккумуляторами.

Читайте также