Skip to content

Класс как объединение функций

В мире Python функции — это первоклассные объекты: их можно передавать, хранить в переменных, возвращать из других функций. Но когда набор функций логически связан — например, все они оперируют над одними и теми же данными — имеет смысл объединить их в класс. Класс выступает в роли контейнера или неймспейса, в котором функции (ныне — методы) становятся методами одного объекта.

Почему классы вместо набора свободных функций?

  • Организация кода: вместо множества отдельных функций мы получаем единый объект с набором методов obj.method().
  • Инкапсуляция: общие детали (например, проверка деления на ноль) можно держать внутри класса, а не дублировать.
  • Гибкость: легко расширять, переопределять и комбинировать функции через наследование и декораторы.

Набор функций

Допустим, у нас есть набор независимых функций для арифметики:

python
def addition(a, b):
    """Return sum of a and b."""
    return a + b

def subtraction(a, b):
    """Return difference of a and b."""
    return a - b

def multiplication(a, b):
    """Return product of a and b."""
    return a * b

def division(a, b):
    """Return quotient of a and b, raises if b == 0."""
    if b == 0:
        raise ValueError("Division by zero")
    return a / b

Описание кода:

  • Каждая функция принимает два аргумента a и b и возвращает результат соответствующей операции.
  • Для division добавлена защита от деления на ноль через выброс ValueError.
  • Такие функции удобно использовать по отдельности, но при увеличении их числа поддерживать код становится сложнее.

Функции в классе

Сгруппируем эти функции внутри класса Operator. Обратите внимание: здесь методы объявлены без параметра self.

python
class Operator:
    """Calculator groups basic arithmetic operations."""

    def addition(a, b):
        """Return sum of a and b."""
        return a + b

    def subtraction(a, b):
        """Return difference of a and b."""
        return a - b

    def multiplication(a, b):
        """Return product of a and b."""
        return a * b

    def division(a, b):
        """Return quotient of a and b, raises if b == 0."""
        if b == 0:
            raise ValueError("Division by zero")
        return a / b


operator = Operator                     # без скобок — ссылка на класс, не экземпляр
print(operator.addition(10, 2))         # 12
print(operator.subtraction(10, 2))      # 8
print(operator.multiplication(10, 2))   # 20
print(operator.division(10, 2))         # 5.0

print(Operator.division(10, 2))         # 5.0 — напрямую по имени класса

Особенности реализации:

  • Методы объявлены без self, поэтому при вызове Python воспринимает a и b как первые два аргумента.
  • operator = Operator — здесь мы не создаём экземпляр, а просто присваиваем переменной сам класс.
  • Вызов operator.addition(10, 2) эквивалентен Operator.addition(10, 2).
  • Такая схема может пригодиться для простого объединения функций, но лишена привычной семантики экземпляров.

Функции в классе + self

Чтобы методы работали как истинные методы экземпляра, нужно добавить параметр self и создать объект с помощью скобок:

python
class Operator:
    """Calculator groups basic arithmetic operations."""

    def addition(self, a, b):
        """Return sum of a and b."""
        return a + b

    def subtraction(self, a, b):
        """Return difference of a and b."""
        return a - b

    def multiplication(self, a, b):
        """Return product of a and b."""
        return a * b

    def division(self, a, b):
        """Return quotient of a and b, raises if b == 0."""
        if b == 0:
            raise ValueError("Division by zero")
        return a / b


operator = Operator()                   # скобки — создаём экземпляр класса
print(operator.addition(10, 2))         # 12
print(operator.subtraction(10, 2))      # 8
print(operator.multiplication(10, 2))   # 20
print(operator.division(10, 2))         # 5.0

Что изменилось?

  • Первый параметр каждого метода — self — ссылка на текущий экземпляр operator.
  • Вызов operator.addition(10, 2) автоматически передаёт внутрь self, а a=10, b=2.
  • Благодаря self в будущем мы сможем хранить состояние внутри объекта.

Статические методы

Когда метод не нуждается ни в атрибутах экземпляра, ни в атрибутах класса, его можно пометить как @staticmethod.

python
class Operator:
    """Calculator groups basic arithmetic operations."""

    @staticmethod
    def addition(a, b):
        """Return sum of a and b."""
        return a + b

    @staticmethod
    def subtraction(a, b):
        """Return difference of a and b."""
        return a - b

    @staticmethod
    def multiplication(a, b):
        """Return product of a and b."""
        return a * b

    @staticmethod
    def division(a, b):
        """Return quotient of a and b, raises if b == 0."""
        if b == 0:
            raise ValueError("Division by zero")
        return a / b


operator = Operator()                   # экземпляр, но статические методы можно вызвать любым способом
print(operator.addition(10, 2))         # 12
print(operator.subtraction(10, 2))      # 8
print(operator.multiplication(10, 2))   # 20
print(operator.division(10, 2))         # 5.0

print(Operator.division(10, 2))         # 5.0 — так же срабатывает напрямую по имени класса

Особенности @staticmethod:

  • Аннотация отключает передачу self.
  • Статические методы в поведении эквивалентны обычным функциям внутри модуля, но логически сгруппированы в классе.

Упражнения

Решения должны содержать полное оформление: явную типологию и описание.

  1. Дополните класс Operator методами:

    • pow(a, b), возведение в степень.
    • mod(a, b), возвращает остаток от деления.
    • floordiv(), возвращает результат целочисленного деления.
    • и протестируйте его вызовами с экземпляром и через имя класса.
  2. Создайте класс Converter с @staticmethod, который переводит температуру в разные единицы измерения:

    • fahrenheit_to_celsius(fahrenheit) - °F в °C по формуле: °C = (°F – 32) × 5 ÷ 9.
    • fahrenheit_to_kelvin(fahrenheit) - °F в K по формуле: K = (°F – 32) × 5 ÷ 9 + 273,15.
    • celsius_to_kelvin(celsius) - °C в K по формуле: K = °C + 273.15.
    • celsius_to_fahrenheit(celsius) - °C в °F по формуле: °F = °C × 9 ÷ 5 + 32.
    • kelvin_to_celsius(celsius) - K в °C по формуле: °C = K – 273,15.
    • kelvin_to_farenheit(celsius) - K в °F по формуле: °F = (K – 273,15) × 9 ÷ 5 + 32

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

    python
    print(Converter.fahrenheit_to_celsius(212))      # 100.0
    print(Converter.celsius_to_fahrenheit(0))        # 32.0
    print(Converter.kelvin_to_celsius(273.15))       # 0.0
    print(Converter.fahrenheit_to_kelvin(-40))       # 233.15
    print(Converter.kelvin_to_fahrenheit(373.15))    # 212.0
  3. Создайте класс проверки строки как числа со статическими методами проверки:

    • целое положительное число,
    • целое отрицательное число,
    • целое число,
    • дробное положительное число,
    • дробное отрицательное число,
    • дробное число,
    • положительное число,
    • отрицательное число,
    • число,
    • число ноль.

    Проверьте верность работы методов. Используйте данный класс проверки строки как числа для решения калькулятора полей.

  4. Создайте класс с методами проверки строки для калькулятора котенка:

    • проверка название товара: не менее двух символов (минимум одной буквы с одно цифрой или буквы),
    • проверка количества товара: целое положительное число, не ноль,
    • проверка цены товара: положительное число, не более двух знаков после запятой,
    • проверка дня: целое положительное число, не ноль,
    • проверка года: целое или половинное число, не ноль. И используйте класс в решении программы калькулятора котенка.