Саммерфилд - Программирование на Python 3 (1077331), страница 62
Текст из файла (страница 62)
Прежде чем приступать к обсуждению всех его методов, рассмотрим несколько примеров их использования: Собственные классы 281 а вернет: '(-19, 4)' а вернет: (Ра1ав, Тгов) атг(Ь) а==Ь, а!=Ь Класс Рс(лт имеет два атрибута данных, ае1т, х и ае1т, у, и пять методов (не считая унаследованных методов), из которых четыре являются специальными методами — они показаны на рис. б.2. После импортирования модуля ЗЬаре появляется возможность использовать класс Рс1лт, как любой другой класс.
Доступ к атрибутам можно осуществлять непосредственно (например, у = а, у), а сам класс отлично интегрируется со всеми остальными классами языка РуФ)топ, обеспечивая поддержку оператора равенства (==) и представления класса в репрезентативной и строковой формах.
Интерпретатор РуФ)топ достаточно умен, чтобы обеспечить поддержку оператора неравенства (! =) на основе имеющейся поддержки оператора равенства. (Однако имеется возможность реализовать поддержку каждого оператора в отдельности, если потребуется обеспечить полный контроль, когда, к примеру, один оператор не является полной противоположностью другому.) При вызове метода интерпретатор автоматически передает ему первый аргумент — ссылку на сам объект (в языках С++ и дача она имеет имя М(а).
В соответствии с соглашениями мы обязаны включать этот параметр в список под именем ае)т. Все атрибуты объекта (данные и методы) должны квалифицироваться именем ве1г. При этом потребуется вводить с клавиатуры чуть больше, чем в других языках программирования, но в этом есть свое преимущество — полная ясность: мы всегда точно знаем, что обращаемся к атрибуту объекта, если квалифицируем его именем ае1г. Чтобы создать объект, необходимо выполнить два действия. Сначала необходимо создать неинициализированную заготовку объекта, а затем необходимо подготовить объект к использованию, инициализировав Рас.
6.2. Дерево наследования класса Ро(ят 282 Глава б. Обьектно-ориентированное программирование его. В некоторых языках программирования (таких как С++ и дача) эти два действия объединены в одно, но в языке РуФ)топ они выполняются отдельно друг от друга.
Когда создается объект (например, р Иваре.Ротс!()), то сначала вызывается специальный метод пеи (), который создает объект, а затем выполняется инициализация объекта вызовом специального метода та(т (). В языке Ру!)топ при создании практически любого класса нам будет необходимо переопределять только метод !и!т (), посколькуимеющейсяреализацииметодаоЬ- )ест. пеи () почти всегда достаточно, и к тому же он вызывается автоматически, если мы не предусматриваем собственную реализацию метода пеи ().
(Ниже в этой главе мы увидим пример одного из редких случаев, когда возникает необходимость переопределить метод пеи ().) Отсутствие необходимости переопределять методы в подклассе — это еще одно преимущество объектно-ориентированного программирования. Если метод базового класса удовлетворяет нашим потребностям, мы можем не переопределять его в своем классе. Если при обращении к методу объекта окажется, что класс объекта не реализует его, интерпретатор автоматически попытается отыскать его в базовых классах объекта, а затем в их базовых классах, и так до тех пор, пока не найдет требуемый метод, а если метод не будет обнаружен, он возбудит исключение Апг!Ьстебггог.
Например, если попробовать выполнить инструкцию р = ЗЬаре. Ро!пт(), интерпретатор начнет поиск метода Ротс!. пеи (). Поскольку мы не переопределяли этот метод, интерпретатор попытается отыскать этот метод в базовом классе класса Ро!пт. В данном случае существует всего один базовый класс, ЬЬ)ест, который имеет требуемый метод, поэтому интерпретатор вызовет метод ЬЬ)ест. пеи () и создаст неинициализированную заготовку объекта. Затем интерпретатор приступит к поиску метода инициализации, 1п!1 (), и поскольку мы предусмотрели его реализацию, интерпретатору не потребуется искать его в базовых классах и он вызовет метод Ро1пт.
1п!т (). В заключение интерпретатор запишет в переменную р ссылку на вновь созданный и инициализированный объект типа Ротпт. Поскольку методы очень короткие и к тому же они приводились за несколько страниц отсюда, для удобства мы приведем снова каждый метод перед обсуждением. пег тптт (ве11, х=е, у=о): ве1т.х = х ае1Г у = у В методе инициализации создаются две переменные экземпляра, ве!Г. х и ве11. у, которым присваиваются значения параметров х и у. Посколь- гвз Собственные классы ку при создании нового объекта класса Рстпт интерпретатор сразу же обнаружит этот метод, он не будет автоматически вызывать метод сЬ)ест.
1п!т (). Как только интерпретатор обнаруживает необходимый метод, он сразу же вызывает его, прекращая дальнейшие поиски. сег ствтвпсе ггоа ог!91п(ве1г). гетогп зато.пуррт(ве1(.х, ве1г.у) Это обычный метод, выполняющий вычисления на основе переменных экземпляра объекта. Для методов весьма характерно иметь небольшой размер и получать в виде параметров только объект, в контексте которого они вызываются, поскольку нередко все данные, необходимые методу, доступны внутри объекта. оег ец (ве1(, о!пег): гетогп ве1г.х == с!пег.х впс ве)т.у == отвес.у Имена методов не должны начинаться и заканчиваться двумя симво- лами подчеркивания, если они не являются предопределенными спе- циальными методами.
В языке РуФ)топ каждому оператору сравнения соответствует свой специальный метод, как показано в табл. 6.1. Таблица 6.1. Специальные методы сравнивания Специальный метод Пример использования Описание Возвращает Тгое, если х мень- ше, чем у 11 (ве1(, отвес) х < у Возвращает Тгое, если х мень- ше ияи равно у 1е (ве11, о!Пег) х <= у ец (ве11, о!пег) пе (ве11. с!пег) Возвращает Тгое, если х равно у х == у Возвращает Тгое, если х ие рав- но у х!= у де (ве1(, о!пег) Возвращает Тгое, если х больше или равно у х >= у Возвращает Тгое, если х боль- ше. чем у дт (ве1(, о!пег) х > у Пуристы объектно-ориентированного программирования могли бы начать реализацию своего метода с вызова метода 1шт () базового класса, обращением к ворег().
тп11 (). Действие вызова такой функции ворег() заключается в вызове метода 1птт () базового класса. Для классов, порожденных непосредственно от класса оо)ест, в этом нет никакой необходимости, и в этой книге мы будем вызывать методы базовых классов, только когда это действительно нужно — например, при создании классов, которые должны будут наследоваться, или при создании классов, не являющихся непосредственными наследниками класса оЬ)ест. Отчасти это вопрос стиля, но тем не менее совершенно разумно — всегда начинать метод 1п11 () своего класса с вызова ворег(). Тптт (). Глава 6. Объектно-ориентированное программирование 284 Все экземпляры классов по умолчанию поддерживают оператор == и операция сравнения всегда возвращает Еа1ве. Мы можем переопределить это поведение, реализовав специальный метод ер (), как это было сделано в данном случае.
Интерпретатор Ру1Ьоп будет автоматически подставлять метод пе ( ) (по1 ет(па1 — не равно) реализующий действие оператора неравенства (! =), если в классе присутствует реализация метода ео (), но отсутствует реализация метода пе (). По умолчанию все экземпляры классов являются хешируемыми, поэтому для них можно вызывать функцию Пава(), использовать их в качестве ключей словаря и сохранять в множествах. Но если будет реализован метод ео (), экземпляры перестанут быть хешируемыми. Как исправить это положение, будет показано при обсуждении класса Ротаувоо1 ниже.
а Тип Реххуаоо1, стр. 292 Оет герт (ве!т): гетсгп "Ротпт((в.х,'г), (О.у'г))".гогаат(ве11) Реализовав этот специальный метод, мы получаем возможность сравнивать объекты Ро(пт, но при попытке сравнить объект Ро1пт с объектом другого типа, например, тп1, будет возбуждено исключение А11г1- ОптеЕггог (поскольку объекты класса тпт не имеют атрибута х).
С другой стороны, мы можем сравнивать объекты Ротпт с другими объектами совместимых типов, у которых имеется атрибут х (благодаря грубому определению типов в языке Ру(Ьоп), но это может приводить к неожиданным результатам. Если необходимо избежать сравнения в случаях, когда это не имеет смысла, можно использовать несколько подходов. Один из них состоит в использовании инструкции аввег1, например, аввегт 1в(пвтапсе(о1Пег, Ро1пт).
Другой состоит в том, чтобы возбуждать исключение ТуреЕггог для обозначения попытки сравнения с неподдерживаемым типом, например, 11 по1 (в1пвтапсе(отаег, Ро1п1): га(ве ТуреЕггог(). Третий способ (который, с точки зрения языка Ру1Ьоп, является наиболее правильным) заключается в следующем: т( по1 1втпв1апсе(отлег, Ротпт); гЕ1ОГп (тО11вр1еаеп1еа.
В этом третьем случае, когда метод возвращает (то11ар1еаептеа, интерпретатор попытается вызвать метод о1Пег. еа (ве11), чтобы определить, поддерживает ли тип о1Пег сравнение с типом Ро1пт, и если в этом типе не будет обнаружен такой метод или он также возвращает но11эр!еаеп1ее, интерпретатор возбудит исключение ТуреЕг гог. (Обратите внимание, что значение по11ар1еаептее может вернуть только переопределенный специальный метод сравнения — из тех, что перечислены в табл.