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

Оператор деления по модулю в Python: как работает % и где применяется

Алексей Иванов

Оператор % возвращает остаток от деления одного числа на другое. Если разделить 10 на 3, получим 3 целых и 1 в остатке — именно эту единицу и вычисляет выражение 10 % 3. Несмотря на внешнюю простоту, оператор покрывает десятки практических задач: проверку чётности, циклическую адресацию, перевод секунд в часы и минуты, валидацию данных. В этой статье разберём механику вычисления, подводные камни с отрицательными числами и типом float, а также покажем реальные сценарии использования.

Формула вычисления остатка

Python связывает оператор % с целочисленным делением // через инвариант:

a == (a // b) * b + (a % b)

Выражение a // b возвращает результат деления, округлённый вниз (floor division). Остаток — то, что нужно прибавить к произведению частного на делитель, чтобы получить исходное число. Инвариант работает для любых комбинаций знаков операндов и для типа float. Именно из этой формулы следует поведение % с отрицательными числами.

>>> 17 // 5
3
>>> 17 % 5
2
>>> 3 * 5 + 2
17

Частное равно 3, остаток равен 2. Проверка: 3 × 5 + 2 = 17.

Базовые примеры с целыми числами

Оператор работает с целыми числами (int) и возвращает int:

>>> 10 % 3 → 1
>>> 15 % 5 → 0
>>> 7 % 2 → 1
>>> 100 % 10 → 0

Если делимое меньше делителя, остаток равен самому делимому:

>>> 3 % 7
3

Логика проста: 3 // 7 даёт 0, а 0 × 7 + 3 = 3. Частное нулевое, поэтому всё число уходит в остаток. Если оба операнда одинаковы, остаток всегда нулевой: 5 % 5 = 0.

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

Самое частое применение — проверка, делится ли число нацело. Если x % y == 0, значит x делится на y без остатка. Так определяют чётность:

def is_even(n):
    return n % 2 == 0

В книге «Python для всех» Чарльза Северанса сказано: оператор % позволяет узнать, делится ли одно число на другое, проверив, равен ли остаток нулю. Аналогично определяют кратность трём, пяти, любому числу.

Извлечение цифр из числа

Выражение x % 10 возвращает последнюю цифру, x % 100 — две последние:

>>> 12345 % 10 → 5
>>> 12345 % 100 → 45
>>> 12345 % 1000 → 345

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

Поведение с отрицательными числами

Python отличается от C, C++ и Java: остаток всегда принимает знак делителя (второго операнда). Причина — оператор // округляет к минус бесконечности (floor), а не к нулю.

>>> -7 % 3 → 2
>>> 7 % -3 → -2
>>> -7 % -3 → -1

Разбор первого примера: -7 // 3 даёт -3 (floor от -2.33). Проверяем: -3 × 3 + 2 = -7. Остаток равен 2, а не -1, как было бы в C.

Правило: результат % лежит в диапазоне от 0 до b-1 при положительном b, и от b+1 до 0 при отрицательном b. Если нужен остаток со знаком делимого — math.fmod():

Выражение

Python %

math.fmod()

C/Java %

-7 % 3

2

-1.0

-1

7% -3

-2

1.0

1

-10 % 3

2

-1.0

-1

Оператор % с числами float

Формула та же: a - (a // b) * b:

>>> 7.5 % 2.5 → 0.0
>>> 7.5 % 2.0 → 1.5
>>> 10.3 % 3.1 → 0.9999999999999996

Третий пример демонстрирует ограничение IEEE 754: не все десятичные дроби представимы точно в двоичном формате. Для финансовых расчётов используют модуль decimal.

Функция divmod()

Встроенная функция divmod(a, b) возвращает кортеж из частного и остатка за одну операцию:

>>> divmod(17, 5) → (3, 2)
>>> divmod(-7, 3) → (-3, 2)
>>> divmod(7.5, 2.5) → (3.0, 0.0)

Вместо двух вычислений (a // b и a % b) выполняется одно. Для больших целых чисел divmod() может оказаться быстрее пары отдельных операций. Функция принимает int и float, но не complex — будет TypeError.

Перевод единиц времени

Классическая задача — перевод секунд в часы, минуты и секунды:

total_seconds = 7384

hours, remaining = divmod(total_seconds, 3600)
minutes, seconds = divmod(remaining, 60)
>>> f'{hours}ч {minutes}мин {seconds}с'
'2ч 3мин 4с'

Тот же подход применяют для конвертации байтов в килобайты/мегабайты, копеек в рубли. Каждый раз работает одна пара: // для «крупных единиц» и % для «мелкого остатка».

Циклическая адресация

Оператор % превращает линейный индекс в циклический. Если список содержит n элементов, i % n всегда даёт допустимый индекс от 0 до n-1:

days = ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс']

>>> days[0 % 7] → 'Пн'
>>> days[7 % 7] → 'Пн'
>>> days[10 % 7] → 'Чт'

День 0 и день 7 — одинаковый понедельник. Индекс никогда не выходит за границы. Приём применяют в кольцевых буферах, при раздаче карт игрокам по кругу, при нормализации угла (0°–360°), при выборе текущего элемента зацикленного плейлиста.

Чередование действий в циклах

Оператор % помогает выполнять действие через равные интервалы:

for i in range(10000):
    if i % 100 == 0:
        print(f'Обработано {i} записей')

Или чередовать цвета строк таблицы:

for i, row in enumerate(data):
    color = 'white' if i % 2 == 0 else 'gray'

Условие i % n == 0 срабатывает ровно каждую n-ю итерацию.

Валидация контрольных сумм

Алгоритмы проверки банковских карт (Луна), штрихкодов (EAN-13), ИНН, СНИЛС используют остаток от деления:

if checksum % 10 == 0:
    print('Номер корректен')

Алгоритм Луна суммирует цифры номера карты с определёнными весами, а затем проверяет, делится ли итоговая сумма на 10 без остатка.

Шифр Цезаря

Простейший шифр сдвигает букву алфавита на фиксированное число позиций. Оператор % обеспечивает «заворачивание» в начало:

def caesar_encrypt(text, shift):
    result = []
    for char in text:
        if char.isalpha():
            base = ord('a') if char.islower() else ord('A')
            shifted = (ord(char) - base + shift) % 26
            result.append(chr(base + shifted))
        else:
            result.append(char)
    return ''.join(result)
>>> caesar_encrypt('xyz', 3)
'abc'

Выражение % 26 гарантирует результат от 0 до 25 — без условных конструкций.

Разбиение на группы

Распределить элементы по n группам «по кругу»:

items = list(range(10))
n_groups = 3
groups = [[] for _ in range(n_groups)]

for i, item in enumerate(items):
    groups[i % n_groups].append(item)
>>> groups
[[0, 3, 6, 9], [1, 4, 7], [2, 5, 8]]

Подход используют при параллельной обработке данных: каждый воркер получает свою «порцию» задач.

Магический метод __mod__

В пользовательских классах поведение % определяется методом __mod__:

class Angle:
    def __init__(self, degrees):
        self.degrees = degrees % 360

    def __mod__(self, other):
        return Angle(self.degrees % other)

    def __repr__(self):
        return f'Angle({self.degrees}°)'
>>> Angle(450) → Angle(90°)
>>> Angle(450) % 180 → Angle(90°)

Обратный метод __rmod__ вызывается, когда экземпляр класса стоит справа от оператора.

Оператор % и строки (устаревшее форматирование)

Если левый операнд — строка, % становится оператором форматирования:

>>> 'Мне %d лет' % 25
'Мне 25 лет'

Python определяет поведение по типу левого операнда: строка — форматирование, число — остаток. Сегодня рекомендуют f-строки, но старый стиль встречается в модуле logging и унаследованном коде.

Деление на ноль

Выражение x % 0 вызывает ZeroDivisionError — как и обычное деление. Исключение бросается и для float: 10.0 % 0.0 тоже ошибка, а не NaN.

Модулярное возведение в степень

Встроенная функция pow() принимает третий аргумент — модуль:

>>> pow(2, 10, 1000)
24

pow(a, b, mod) вычисляет (a ** b) % mod через алгоритм быстрого модулярного возведения. Для больших чисел разница в скорости — тысячи раз. Функция активно применяется в криптографии (RSA, Диффи-Хеллман).

Частые ошибки

  • Путать % с  делением. Оператор возвращает остаток,  не частное.
  • Ожидать  отрицательный остаток. В Python -5 % 3 =  1, а не -2.
  • Использовать % для  форматирования строк в новом коде.  F-строки быстрее.
  • Забывать  о погрешности float. Выражение 0.3 %  0.1 не даёт точный 0.0.
  • Не  обрабатывать деление на ноль. x %  0 выбросит исключение при любом x.

FAQ

Чем отличается % от //?

// возвращает частное, % — остаток. divmod(a, b) возвращает оба значения в кортеже.

Работает ли % с комплексными числами?

Нет. Выражение (3+2j) % 2 вызовет TypeError. Оператор определён только для int и float.

Когда использовать math.fmod()?

Когда нужен остаток со знаком числителя, как в C. Это бывает при портировании алгоритмов из C/C++.

Как ускорить (a ** b) % mod?

Использовать pow(a, b, mod) — встроенная функция работает на порядки быстрее раздельных операций.

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