Перезагрузка операторов в классах языка Python

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

Основные идеи, лежащие в основе механизма перегрузки операторов

  • Имена методов, начинающиеся и заканчивающиеся двумя символами подчеркивания (__X__), имеют специальное назначение. Перегрузка операторов в языке Python реализуется за счет создания методов со специаль ными именами для перехватывания операций. Язык Python определяет фиксированные и неизменяемые имена методов для каждой из операций.
  • Такие методы вызываются автоматически, когда экземпляр участвует во встроенных операциях. Например, если объект экземпляра наследует метод __add__, этот метод будет вызываться всякий раз, когда объект будет появляться в операции сложения (+). Возвращаемое значение метода становится результатом соответствующей операции.
  • Классы могут переопределять большинство встроенных операторов. Существует множество специальных имен методов для реализации перегрузки почти всех операторов, доступных для встроенных типов. Сюда входят операторы выражений, а также такие базовые операции, как вывод и создание объекта.
  • В методах перегрузки операторов не существует аргументов со значениями по умолчанию, и ни один из таких методов не является обязательным для реализации. Если класс не определяет и не наследует методы перегрузки операторов, это означает лишь, что экземпляры класса не поддерживают эти операции. Например, если отсутствует метод __add__, попытка выполнить операцию сложения + будет приводить к возбуждению исключения.
  • Операторы позволяют интегрировать классы в объектную модель языка Python. Благодаря перегрузке операторов, объекты, реализованные на базе классов, действуют подобно встроенным типам и тем самым обеспечивают непротиворечивые и совместимые интерфейсы.

Перегрузка операторов является необязательной функциональной особенностью – она используется в основном специалистами, создающими инструментальные средства для других программистов, а не разработчиками прикладных программ. И честно говоря, вам не стоит использовать ее только потому, что это «круто». Если не требуется, чтобы класс имитировал поведение встроенных типов, лучше ограничиться использованием простых методов. Например, зачем приложению, работающему с базой данных служащих, поддержка таких операторов, как * и +? Методы с обычными именами, такими как giveRaise и promote, обычно более уместны.

Вследствие этого мы не будем далее углубляться в подробности реализации методов перегрузки каждого оператора, доступного в языке Python. Однако имеется один метод перегрузки оператора, который можно встретить практически в любом классе: метод __init__, который известен как конструктор и используется для инициализации состояния объектов. Методу __init__ следует уделить особое внимание, потому что он, наряду с аргументом self, является одним из ключей к пониманию ООП в языке Python.

Пример

На этот раз мы определим подкласс, производный от SecondClass и реализую-
щий три специальных метода, которые будут вызываться интерпретатором ав-
томатически:

  • Метод __init__ вызывается, когда создается новый объект экземпляра (ар-
    гумент self представляет новый объект ThirdClass).
  • Метод __add__ вызывается, когда экземпляр ThirdClass участвует в опера-
    ции +
  • Метод __str__ вызывается при выводе объекта (точнее, когда он преобразу-
    ется в строку для вывода вызовом встроенной функции str или ее эквива-
    лентом внутри интерпретатора).

Кроме того, новый подкласс определяет метод с обычным именем mul, который
изменяет сам объект в памяти. Ниже приводится определение нового подкласса:

>>> class ThirdClass(SecondClass): # Наследует SecondClass
... def __init__(self, value): # Вызывается из ThirdClass(value)
... self.data = value
... def __add__(self, other): # Для выражения “self + other”
... return ThirdClass(self.data + other)
... def __str__(self): # Вызывается из print(self), str()
... return ‘[ThirdClass: %s]’ % self.data
... def mul(self, other): # Изменяет сам объект: обычный метод
... self.data *= other
...
>>> a = ThirdClass(“abc”) # Вызывается новый метод __init__
>>> a.display() # Унаследованный метод
Current value = “abc”
>>> print(a) # __str__: возвращает строку
[ThirdClass: abc]
>>> b = a + ‘xyz’ # Новый __add__: создается новый экземпляр
>>> b.display()
Current value = “abcxyz”
>>> print(b) # __str__: возвращает строку
[ThirdClass: abcxyz]
>>> a.mul(3) # mul: изменяется сам экземпляр
>>> print(a)
[ThirdClass: abcabcabc]