Давайте рассмотрим основное отличие классов и модулей: перегрузку операторов. Говоря простым языком, перегрузка операторов позволяет объектам, созданным из классов, перехватывать и участвовать в операциях, которые применяются к встроенным типам: сложение, получение среза, вывод, квалификация имен и так далее. По большей части это автоматический механизм: при выполнении выражений и других встроенных операций интерпретатор передает управление реализации классов. В модулях нет ничего подобного: модули могут реализовать функции, но не операторы выражений.
Основные идеи, лежащие в основе механизма перегрузки операторов
- Имена методов, начинающиеся и заканчивающиеся двумя символами подчеркивания (__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]