>>> hunted1 = QuestionQuote('Bugs Bunny', "What's up, doc")
>>> print(hunted1.who(), 'says:', hunted1.says())
Bugs Bunny says: What's up, doc?
>>> hunted2 = ExclamationQuote('Daffy Duck', "It's rabbit season")
>>> print(hunted2.who(), 'says:', hunted2.says())
Daffy Duck says: It's rabbit season!
Три разные версии метода says() обеспечивают разное поведение трех классов. Так выглядит традиционный полиморфизм в объектно-ориентированных языках. Python пошел немного дальше и позволяет вам вызывать методы who() и says() для любых объектов, включающих эти методы. Определим класс BabblingBrook, который не имеет никакого отношения к нашим охотнику и его жертвам (наследникам класса Quote), созданным ранее:
>>> class BabblingBrook():
…·····def who(self):
…·········return 'Brook'
…·····def says(self):
…·········return 'Babble'
…
>>> brook = BabblingBrook()
Теперь запустим методы who() и says() разных объектов, один из которых (brook) совершенно не связан с остальными:
>>> def who_says(obj):
…·····print(obj.who(), 'says', obj.says())
…
>>> who_says(hunter)
Elmer Fudd says I'm hunting wabbits.
>>> who_says(hunted1)
Bugs Bunny says What's up, doc?
>>> who_says(hunted2)
Daffy Duck says It's rabbit season!
>>> who_says(brook)
Brook says Babble
Такое поведение иногда называется
Особые методы
Теперь вы можете создавать и использовать простые объекты, но опустимся немного глубже и сделаем нечто большее.
Когда вы пишете что-то вроде a = 3 + 8, откуда целочисленные объекты со значениями 3 и 8 узнают, как реализовать операцию +? Кроме того, откуда a знает, как использовать =, чтобы получить результат? Вы можете воспользоваться этими операторами, применяя
Имена этих методов начинаются с двойных подчеркиваний (__) и заканчиваются ими. Вы уже видели один такой метод: __init__ инициализирует только что созданный объект с помощью описания его класса и любых аргументов, которые были переданы в этот метод.
Предположим, у вас есть простой класс Word и вы хотите написать для него метод equals(), который сравнивает два слова, игнорируя регистр. Так и есть, объект класса Word, содержащий значение 'ha', будет считаться равным другому объекту, который содержит значение 'HA'.
В следующем примере показана наша первая попытка, где мы вызываем обычный метод equals(). self.text — это текстовая строка, которую содержит объект класса Word, метод equals() сравнивает ее с текстовой строкой, содержащейся в объекте word2 (другой объект класса Word):
>>> class Word():
…····def __init__(self, text):
…········self.text = text
…
…····def equals(self, word2):
…········return self.text.lower() == word2.text.lower()
…
Далее создадим три объекта Word с помощью трех разных текстовых строк:
>>> first = Word('ha')
>>> second = Word('HA')
>>> third = Word('eh')
Когда строки 'ha' и 'HA' сравниваются в нижнем регистре, они должны быть равными:
>>> first.equals(second)
True
Но строка 'eh' не совпадет со строкой 'ha':
>>> first.equals(third)
False
Мы определили метод equals(), который выполняет преобразование строки в нижний регистр и сравнение. Однако было бы здорово, если бы мы могли просто сказать first == second, как в случае встроенных типов Python. Реализуем такую возможность. Мы изменим имя метода equals() на особое имя __eq__() (вы узнаете, зачем я это сделал, через минуту):
>>> class Word():
…·····def __init__(self, text):
…·········self.text = text
…·····def __eq__(self, word2):
…·········return self.text.lower() == word2.text.lower()
…
Проверим, как это работает:
>>> first = Word('ha')