Изучите декораторы классов

PythonPythonBeginner
Практиковаться сейчас

This tutorial is from open-source community. Access the source code

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

В этом практическом занятии (лабораторной работе) вы узнаете о декораторах классов в Python и повторите и расширите знания о дескрипторах Python. Объединяя эти концепции, вы сможете создавать мощные и чистые структуры кода.

В этом практическом занятии вы будете основываться на предыдущих концепциях дескрипторов и расширять их с использованием декораторов классов. Это сочетание позволяет создавать более чистый и поддерживаемый код с расширенными возможностями валидации. Файлы, которые необходимо изменить, это validate.py и structure.py.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/AdvancedTopicsGroup(["Advanced Topics"]) python(("Python")) -.-> python/BasicConceptsGroup(["Basic Concepts"]) python(("Python")) -.-> python/ControlFlowGroup(["Control Flow"]) python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python(("Python")) -.-> python/ObjectOrientedProgrammingGroup(["Object-Oriented Programming"]) python/BasicConceptsGroup -.-> python/type_conversion("Type Conversion") python/ControlFlowGroup -.-> python/conditional_statements("Conditional Statements") python/FunctionsGroup -.-> python/function_definition("Function Definition") python/ObjectOrientedProgrammingGroup -.-> python/classes_objects("Classes and Objects") python/ObjectOrientedProgrammingGroup -.-> python/inheritance("Inheritance") python/ObjectOrientedProgrammingGroup -.-> python/encapsulation("Encapsulation") python/ObjectOrientedProgrammingGroup -.-> python/class_static_methods("Class Methods and Static Methods") python/AdvancedTopicsGroup -.-> python/decorators("Decorators") subgraph Lab Skills python/type_conversion -.-> lab-132516{{"Изучите декораторы классов"}} python/conditional_statements -.-> lab-132516{{"Изучите декораторы классов"}} python/function_definition -.-> lab-132516{{"Изучите декораторы классов"}} python/classes_objects -.-> lab-132516{{"Изучите декораторы классов"}} python/inheritance -.-> lab-132516{{"Изучите декораторы классов"}} python/encapsulation -.-> lab-132516{{"Изучите декораторы классов"}} python/class_static_methods -.-> lab-132516{{"Изучите декораторы классов"}} python/decorators -.-> lab-132516{{"Изучите декораторы классов"}} end

Реализация проверки типов с использованием дескрипторов

На этом этапе мы создадим класс Stock, который будет использовать дескрипторы для проверки типов. Но сначала разберемся, что такое дескрипторы. Дескрипторы - это очень мощная возможность в Python. Они позволяют вам контролировать, как доступ к атрибутам классов осуществляется.

Дескрипторы - это объекты, которые определяют, как происходит доступ к атрибутам других объектов. Они делают это, реализуя специальные методы, такие как __get__, __set__ и __delete__. Эти методы позволяют дескрипторам управлять тем, как атрибуты извлекаются, устанавливаются и удаляются. Дескрипторы очень полезны для реализации валидации, проверки типов и вычисляемых свойств. Например, вы можете использовать дескриптор, чтобы убедиться, что атрибут всегда является положительным числом или строкой определенного формата.

В файле validate.py уже есть классы валидаторов (String, PositiveInteger, PositiveFloat). Мы можем использовать эти классы для валидации атрибутов нашего класса Stock.

Теперь давайте создадим наш класс Stock с использованием дескрипторов.

  1. Сначала откройте файл stock.py в редакторе. Вы можете сделать это, запустив следующую команду в терминале:
code ~/project/stock.py

Эта команда использует редактор code для открытия файла stock.py, расположенного в директории ~/project.

  1. После открытия файла замените заполнитель следующим кодом:
## stock.py

from structure import Structure
from validate import String, PositiveInteger, PositiveFloat

class Stock(Structure):
    _fields = ('name', 'shares', 'price')
    name = String()
    shares = PositiveInteger()
    price = PositiveFloat()

    @property
    def cost(self):
        return self.shares * self.price

    def sell(self, nshares):
        self.shares -= nshares

## Create an __init__ method based on _fields
Stock.create_init()

Разберем, что делает этот код. Кортеж _fields определяет атрибуты класса Stock. Это имена атрибутов, которые будут у объектов нашего класса Stock.

Атрибуты name, shares и price определены как объекты дескрипторов. Дескриптор String() гарантирует, что атрибут name является строкой. Дескриптор PositiveInteger() обеспечивает, чтобы атрибут shares был положительным целым числом. А дескриптор PositiveFloat() гарантирует, что атрибут price является положительным числом с плавающей точкой.

Свойство cost - это вычисляемое свойство. Оно вычисляет общую стоимость акций на основе количества акций и цены за одну акцию.

Метод sell используется для уменьшения количества акций. Когда вы вызываете этот метод с количеством акций для продажи, он вычитает это количество из атрибута shares.

Строка Stock.create_init() динамически создает метод __init__ для нашего класса. Этот метод позволяет нам создавать объекты класса Stock, передав значения для атрибутов name, shares и price.

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

  2. Теперь давайте запустим тесты, чтобы проверить вашу реализацию. Сначала измените текущую директорию на ~/project, запустив следующую команду:

cd ~/project

Затем запустите тесты с помощью следующей команды:

python3 teststock.py

Если ваша реализация правильная, вы должны увидеть вывод, похожий на следующий:

.........
----------------------------------------------------------------------
Ran 9 tests in 0.001s

OK

Этот вывод означает, что все тесты пройдены. Дескрипторы успешно валидируют типы каждого атрибута!

Давайте попробуем создать объект класса Stock в интерпретаторе Python. Сначала убедитесь, что вы находитесь в директории ~/project. Затем запустите следующую команду:

cd ~/project
python3 -c "from stock import Stock; s = Stock('GOOG', 100, 490.1); print(s); print(f'Cost: {s.cost}')"

Вы должны увидеть следующий вывод:

Stock('GOOG', 100, 490.1)
Cost: 49010.0

Вы успешно реализовали дескрипторы для проверки типов! Теперь давайте усовершенствуем этот код еще больше.

✨ Проверить решение и практиковаться

Создание декоратора класса для валидации

На предыдущем этапе наша реализация работала, но была избыточность. Мы должны были указать и кортеж _fields, и атрибуты-дескрипторы. Это не очень эффективно, и мы можем улучшить ситуацию. В Python декораторы классов - это мощный инструмент, который может помочь нам упростить этот процесс. Декоратор класса - это функция, которая принимает класс в качестве аргумента, модифицирует его каким-то образом и затем возвращает модифицированный класс. Используя декоратор класса, мы можем автоматически извлекать информацию о полях из дескрипторов, что сделает наш код чище и более поддерживаемым.

Давайте создадим декоратор класса для упрощения нашего кода. Вот шаги, которые вам нужно выполнить:

  1. Сначала откройте файл structure.py. Вы можете использовать следующую команду в терминале:
code ~/project/structure.py

Эта команда откроет файл structure.py в вашем редакторе кода.

  1. Затем добавьте следующий код в начало файла structure.py сразу после любых инструкций импорта. Этот код определяет наш декоратор класса:
from validate import Validator

def validate_attributes(cls):
    """
    Class decorator that extracts Validator instances
    and builds the _fields list automatically
    """
    validators = []
    for name, val in vars(cls).items():
        if isinstance(val, Validator):
            validators.append(val)

    ## Set _fields based on validator names
    cls._fields = [val.name for val in validators]

    ## Create initialization method
    cls.create_init()

    return cls

Разберем, что делает этот декоратор:

  • Сначала он создает пустой список с именем validators. Затем он проходит по всем атрибутам класса с помощью vars(cls).items(). Если атрибут является экземпляром класса Validator, он добавляет этот атрибут в список validators.
  • Затем он устанавливает атрибут _fields класса. Он создает список имен из валидаторов в списке validators и присваивает его cls._fields.
  • Наконец, он вызывает метод create_init() класса для генерации метода __init__, а затем возвращает модифицированный класс.
  1. После добавления кода сохраните файл structure.py. Сохранение файла гарантирует, что ваши изменения будут сохранены.

  2. Теперь нам нужно изменить файл stock.py, чтобы использовать этот новый декоратор. Откройте файл stock.py с помощью следующей команды:

code ~/project/stock.py
  1. Обновите файл stock.py, чтобы использовать декоратор validate_attributes. Замените существующий код следующим:
## stock.py

from structure import Structure, validate_attributes
from validate import String, PositiveInteger, PositiveFloat

@validate_attributes
class Stock(Structure):
    name = String()
    shares = PositiveInteger()
    price = PositiveFloat()

    @property
    def cost(self):
        return self.shares * self.price

    def sell(self, nshares):
        self.shares -= nshares

Обратите внимание на изменения, которые мы внесли:

  • Мы добавили декоратор @validate_attributes сразу над определением класса Stock. Это сообщает Python применить декоратор validate_attributes к классу Stock.
  • Мы удалили явное объявление _fields, так как декоратор будет обрабатывать его автоматически.
  • Мы также удалили вызов Stock.create_init(), так как декоратор занимается созданием метода __init__.

Как результат, класс теперь проще и чище. Декоратор заботится обо всех деталях, которые мы раньше обрабатывали вручную.

  1. После внесения этих изменений нам нужно убедиться, что все по-прежнему работает как ожидается. Запустите тесты еще раз с помощью следующих команд:
cd ~/project
python3 teststock.py

Если все работает правильно, вы должны увидеть следующий вывод:

.........
----------------------------------------------------------------------
Ran 9 tests in 0.001s

OK

Этот вывод указывает, что все тесты успешно пройдены.

Давайте также протестируем наш класс Stock интерактивно. Запустите следующую команду в терминале:

cd ~/project
python3 -c "from stock import Stock; s = Stock('GOOG', 100, 490.1); print(s); print(f'Cost: {s.cost}')"

Вы должны увидеть следующий вывод:

Stock('GOOG', 100, 490.1)
Cost: 49010.0

Отлично! Вы успешно реализовали декоратор класса, который упрощает наш код, автоматически обрабатывая объявление полей и инициализацию. Это делает наш код более эффективным и легким в поддержке.

✨ Проверить решение и практиковаться

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

На этапе 2 мы создали декоратор класса, который упрощает наш код. Декоратор класса - это особый тип функции, которая принимает класс в качестве аргумента и возвращает модифицированный класс. Это полезный инструмент в Python для добавления функциональности классам без изменения их исходного кода. Однако мы по-прежнему должны явно применять декоратор @validate_attributes к каждому классу. Это означает, что каждый раз, когда мы создаем новый класс, который нуждается в валидации, мы должны помнить добавить этот декоратор, что может быть немного утомительно.

Мы можем улучшить это, автоматически применяя декоратор через наследование. Наследование - это фундаментальное понятие в объектно-ориентированном программировании, при котором подкласс может наследовать атрибуты и методы от родительского класса. Метод __init_subclass__ в Python был введен в версии 3.6, чтобы позволить родительским классам настраивать инициализацию подклассов. Это означает, что когда создается подкласс, родительский класс может выполнить некоторые действия над ним. Мы можем использовать эту возможность для автоматического применения нашего декоратора к любому классу, который наследует от Structure.

Давайте реализуем это:

  1. Откройте файл structure.py:
code ~/project/structure.py

Здесь мы используем команду code для открытия файла structure.py в редакторе кода. Этот файл содержит определение класса Structure, и мы собираемся изменить его, чтобы использовать метод __init_subclass__.

  1. Добавьте метод __init_subclass__ в класс Structure:
class Structure:
    _fields = ()
    _types = ()

    def __init__(self, *args):
        if len(args) != len(self._fields):
            raise TypeError(f'Expected {len(self._fields)} arguments')
        for name, val in zip(self._fields, args):
            setattr(self, name, val)

    def __repr__(self):
        values = ', '.join(repr(getattr(self, name)) for name in self._fields)
        return f'{type(self).__name__}({values})'

    @classmethod
    def create_init(cls):
        '''
        Create an __init__ method from _fields
        '''
        body = 'def __init__(self, %s):\n' % ', '.join(cls._fields)
        for name in cls._fields:
            body += f'    self.{name} = {name}\n'

        ## Execute the function creation code
        namespace = {}
        exec(body, namespace)
        setattr(cls, '__init__', namespace['__init__'])

    @classmethod
    def __init_subclass__(cls):
        validate_attributes(cls)

Метод __init_subclass__ - это метод класса, то есть его можно вызывать на самом классе, а не на экземпляре класса. Когда создается подкласс Structure, этот метод будет автоматически вызван. Внутри этого метода мы вызываем декоратор validate_attributes на подклассе cls. Таким образом, каждый подкласс Structure автоматически получит поведение валидации.

  1. Сохраните файл.

После внесения изменений в файл structure.py мы должны сохранить его, чтобы изменения вступили в силу.

  1. Теперь давайте обновим файл stock.py, чтобы воспользоваться этой новой возможностью:
code ~/project/stock.py

Мы открываем файл stock.py, чтобы изменить его. Этот файл содержит определение класса Stock, и мы собираемся сделать его наследником класса Structure, чтобы использовать автоматическое применение декоратора.

  1. Измените файл stock.py, чтобы удалить явный декоратор:
## stock.py

from structure import Structure
from validate import String, PositiveInteger, PositiveFloat

class Stock(Structure):
    name = String()
    shares = PositiveInteger()
    price = PositiveFloat()

    @property
    def cost(self):
        return self.shares * self.price

    def sell(self, nshares):
        self.shares -= nshares

Обратите внимание, что мы:

  • Удалили импорт validate_attributes, так как нам больше не нужно его явно импортировать, так как декоратор применяется автоматически через наследование.
  • Удалили декоратор @validate_attributes, так как метод __init_subclass__ в классе Structure позаботится о его применении.
  • Теперь код полностью зависит от наследования от Structure для получения поведения валидации.
  1. Запустите тесты еще раз, чтобы убедиться, что все по-прежнему работает:
cd ~/project
python3 teststock.py

Запуск тестов важен, чтобы убедиться, что наши изменения не сломали ничего. Если все тесты проходят, это означает, что автоматическое применение декоратора через наследование работает правильно.

Вы должны увидеть, что все тесты проходят:

.........
----------------------------------------------------------------------
Ran 9 tests in 0.001s

OK

Давайте протестируем наш класс Stock еще раз, чтобы убедиться, что он работает как ожидается:

cd ~/project
python3 -c "from stock import Stock; s = Stock('GOOG', 100, 490.1); print(s); print(f'Cost: {s.cost}')"

Эта команда создает экземпляр класса Stock и выводит его представление и стоимость. Если вывод соответствует ожиданиям, это означает, что класс Stock работает правильно с автоматическим применением декоратора.

Вывод:

Stock('GOOG', 100, 490.1)
Cost: 49010.0

Эта реализация еще более чистая! Используя __init_subclass__, мы избавились от необходимости явно применять декораторы. Любой класс, который наследует от Structure, автоматически получает поведение валидации.

✨ Проверить решение и практиковаться

Добавление функциональности преобразования строк

В программировании часто бывает полезно создавать экземпляры класса из строк данных, особенно когда работаешь с данными из источников, таких как CSV-файлы. В этом разделе мы добавим возможность создавать экземпляры класса Structure из строк данных. Мы сделаем это, реализовав метод класса from_row в классе Structure.

  1. Сначала вам нужно открыть файл structure.py. Именно здесь мы внесем изменения в код. Используйте следующую команду в терминале:
code ~/project/structure.py
  1. Затем мы изменим функцию validate_attributes. Эта функция является декоратором класса, который извлекает экземпляры Validator и автоматически создает списки _fields и _types. Мы обновим ее так, чтобы она также собирала информацию о типах.
def validate_attributes(cls):
    """
    Class decorator that extracts Validator instances
    and builds the _fields and _types lists automatically
    """
    validators = []
    for name, val in vars(cls).items():
        if isinstance(val, Validator):
            validators.append(val)

    ## Set _fields based on validator names
    cls._fields = [val.name for val in validators]

    ## Set _types based on validator expected_types
    cls._types = [getattr(val, 'expected_type', lambda x: x) for val in validators]

    ## Create initialization method
    cls.create_init()

    return cls

В этой обновленной функции мы собираем атрибут expected_type из каждого валидатора и сохраняем его в переменной класса _types. Это будет полезно позже, когда мы будем преобразовывать данные из строк в правильные типы.

  1. Теперь мы добавим метод класса from_row в класс Structure. Этот метод позволит нам создавать экземпляр класса из строки данных, которая может быть списком или кортежем.
@classmethod
def from_row(cls, row):
    """
    Create an instance from a data row (list or tuple)
    """
    rowdata = [func(val) for func, val in zip(cls._types, row)]
    return cls(*rowdata)

Вот как работает этот метод:

  • Он принимает строку данных, которая может быть в виде списка или кортежа.
  • Он преобразует каждое значение в строке в ожидаемый тип, используя соответствующую функцию из списка _types.
  • Затем он создает и возвращает новый экземпляр класса, используя преобразованные значения.
  1. После внесения этих изменений сохраните файл structure.py. Это гарантирует, что ваши изменения в коде будут сохранены.

  2. Давайте протестируем метод from_row, чтобы убедиться, что он работает как ожидается. Мы создадим простой тест, используя класс Stock. Запустите следующую команду в терминале:

cd ~/project
python3 -c "from stock import Stock; s = Stock.from_row(['GOOG', '100', '490.1']); print(s); print(f'Cost: {s.cost}')"

Вы должны увидеть вывод, похожий на следующий:

Stock('GOOG', 100, 490.1)
Cost: 49010.0

Обратите внимание, что строковые значения '100' и '490.1' были автоматически преобразованы в правильные типы (целое число и число с плавающей точкой). Это показывает, что наш метод from_row работает правильно.

  1. Наконец, давайте попробуем прочитать данные из CSV-файла, используя модуль reader.py. Запустите следующую команду в терминале:
cd ~/project
python3 -c "from stock import Stock; import reader; portfolio = reader.read_csv_as_instances('portfolio.csv', Stock); print(portfolio); print(f'Total value: {sum(s.cost for s in portfolio)}')"

Вы должны увидеть вывод, показывающий акции из CSV-файла:

[Stock('GOOG', 100, 490.1), Stock('AAPL', 50, 545.75), Stock('MSFT', 200, 30.47)]
Total value: 73444.0

Метод from_row позволяет нам легко преобразовывать данные из CSV в экземпляры класса Stock. В сочетании с функцией read_csv_as_instances у нас есть мощный способ загружать и работать со структурированными данными.

✨ Проверить решение и практиковаться

Добавление валидации аргументов методов

В Python валидация данных является важной частью написания надежного кода. В этом разделе мы усовершенствуем нашу валидацию, автоматически проверяя аргументы методов. Файл validate.py уже содержит декоратор @validated. Декоратор в Python - это специальная функция, которая может модифицировать другую функцию. Декоратор @validated здесь может проверять аргументы функции на соответствие их аннотациям. Аннотации в Python - это способ добавления метаданных к параметрам функции и возвращаемым значениям.

Давайте изменим наш код, чтобы применить этот декоратор к методам с аннотациями:

  1. Сначала нам нужно понять, как работает декоратор validated. Откройте файл validate.py, чтобы изучить его:
code ~/project/validate.py

Декоратор validated использует аннотации функции для валидации аргументов. Прежде чем разрешить выполнение функции, он проверяет каждый аргумент на соответствие типу, указанному в аннотации. Например, если аргумент аннотирован как целое число, декоратор убедится, что переданное значение действительно является целым числом.

  1. Теперь мы изменим функцию validate_attributes в файле structure.py, чтобы обернуть методы с аннотациями декоратором validated. Это означает, что для любого метода с аннотациями в классе аргументы будут автоматически валидироваться. Откройте файл structure.py:
code ~/project/structure.py
  1. Обновите функцию validate_attributes:
def validate_attributes(cls):
    """
    Class decorator that:
    1. Extracts Validator instances and builds _fields and _types lists
    2. Applies @validated decorator to methods with annotations
    """
    ## Import the validated decorator
    from validate import validated

    ## Process validator descriptors
    validators = []
    for name, val in vars(cls).items():
        if isinstance(val, Validator):
            validators.append(val)

    ## Set _fields based on validator names
    cls._fields = [val.name for val in validators]

    ## Set _types based on validator expected_types
    cls._types = [getattr(val, 'expected_type', lambda x: x) for val in validators]

    ## Apply @validated decorator to methods with annotations
    for name, val in vars(cls).items():
        if callable(val) and hasattr(val, '__annotations__'):
            setattr(cls, name, validated(val))

    ## Create initialization method
    cls.create_init()

    return cls

Эта обновленная функция теперь делает следующее:

  1. Она обрабатывает дескрипторы валидаторов, как и раньше. Дескрипторы валидаторов используются для определения правил валидации атрибутов класса.

  2. Она находит все методы с аннотациями в классе. Аннотации добавляются к параметрам метода, чтобы указать ожидаемый тип аргумента.

  3. Она применяет декоратор @validated к этим методам. Это гарантирует, что аргументы, передаваемые в эти методы, будут валидированы в соответствии с их аннотациями.

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

  5. Теперь давайте обновим метод sell в классе Stock, чтобы добавить аннотацию. Аннотации помогают указать ожидаемый тип аргумента, который будет использован декоратором @validated для валидации. Откройте файл stock.py:

code ~/project/stock.py
  1. Измените метод sell, чтобы добавить аннотацию типа:
## stock.py

from structure import Structure
from validate import String, PositiveInteger, PositiveFloat

class Stock(Structure):
    name = String()
    shares = PositiveInteger()
    price = PositiveFloat()

    @property
    def cost(self):
        return self.shares * self.price

    def sell(self, nshares: PositiveInteger):
        self.shares -= nshares

Основное изменение - это добавление : PositiveInteger к параметру nshares. Это сообщает Python (и нашему декоратору @validated), что этот аргумент должен быть валидирован с использованием валидатора PositiveInteger. Таким образом, когда мы вызываем метод sell, аргумент nshares должен быть положительным целым числом.

  1. Запустите тесты еще раз, чтобы убедиться, что все по-прежнему работает. Запуск тестов - это хороший способ убедиться, что наши изменения не сломали существующую функциональность.
cd ~/project
python3 teststock.py

Вы должны увидеть, что все тесты проходят:

.........
----------------------------------------------------------------------
Ran 9 tests in 0.001s

OK
  1. Давайте протестируем нашу новую валидацию аргументов. Мы попробуем вызвать метод sell с корректными и некорректными аргументами, чтобы проверить, работает ли валидация как ожидается.
cd ~/project
python3 -c "from stock import Stock; s = Stock('GOOG', 100, 490.1); s.sell(25); print(s); try: s.sell(-25); except Exception as e: print(f'Error: {e}')"

Вы должны увидеть вывод, похожий на следующий:

Stock('GOOG', 75, 490.1)
Error: Bad Arguments
  nshares: must be >= 0

Это показывает, что наша валидация аргументов методов работает! Первый вызов sell(25) завершается успешно, потому что 25 - это положительное целое число. Но второй вызов sell(-25) завершается с ошибкой, потому что -25 не является положительным целым числом.

Теперь вы реализовали полную систему для:

  1. Валидации атрибутов класса с использованием дескрипторов. Дескрипторы используются для определения правил валидации атрибутов класса.
  2. Автоматического сбора информации о полях с использованием декораторов класса. Декораторы класса могут изменять поведение класса, например, собирать информацию о полях.
  3. Преобразования строковых данных в экземпляры. Это полезно при работе с данными из внешних источников.
  4. Валидации аргументов методов с использованием аннотаций. Аннотации помогают указать ожидаемый тип аргумента для валидации.

Это демонстрирует мощь комбинации дескрипторов и декораторов в Python для создания выразительных, самовалидирующихся классов.

✨ Проверить решение и практиковаться

Резюме

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

Эти техники являются мощными инструментами для создания надежного и поддерживаемого Python - кода. Они позволяют вам четко выражать требования к валидации и применять их на всем протяжении вашего кода. Теперь вы можете применять эти шаблоны в своих собственных Python - проектах, чтобы повысить качество кода и уменьшить количество повторяющегося кода.