Лутц М. - Изучаем Python (1077325), страница 126
Текст из файла (страница 126)
Здесь очень важно заметить следующее: специализация, выполненная в классе ЯесопСС1азз, находится полностью за пределами класса ет гзт- Классы адаптируются посредством наследования мы рассматривали в пятой части книги. Например, в одном файле модуля могут находиться определения сразу нескольких классов — подобно другим инструкциям в модуле, инструкции с1авв выполняются в ходе операции импортирования и определяют имена, которые в свою очередь становятся атрибутами модуля.
Вообще любой модуль может содержать самые произвольные сочетания из любого числа переменных, функций и классов, и все эти имена будут вести себя в модуле одинаково. Это демонстрирует файл гоос1.ру: № гооо.ру чаг = т № гооа уаг оег гипс<> № горо гипс с1авв враз: № Горд.арам с1авв Пам. № гооо, пал с1авв еддв № Грос еддв Это правило остается справедливым, даже если модуль и класс имеют одинаковые имена. Например, пусть имеется следующий файл регвол.ру: с1авв регвоп Чтобы получить доступ к классу, нам необходимо обратиться к модулю, как обычно: тарогт регвоп № Иилортироеать модуль х = регвоп,регвоп() № Класс внутри модуля Хотя этот способ может показаться избыточным, он совершенно необходим: имя регвоп, регвоп ссылается на класс регвоп внутри модуля регвоп.
Если использовать просто имя регвоп, мы обратимся к модулю, а не к классу; кроме случая, когда используется инструкция т гоа: ггом регвоп рпрогт регвоп № Получить класс иа модуля х = регвоп11 № Испольаопать иия класса Как и любые другие переменные, мы не увидим класс в файле модуля, пока не импортируем его или как-то иначе не извлечем класс из вмещающего файла. Если вам это кажется сложным, то не используйте одинаковые имена для модулей и классов в них. Кроме того, имейте в виду: несмотря на то, что классы и модули являются пространствами имен для подключения атрибутов, они представляют собой совершенно разные структуры: модуль является отражением целого файла, а класс — это лишь инструкция внутри файла.
Подробнее об этих различиях мы поговорим позднее в этой части книги. 58б Глава 23. Основы программирования классов Классы могут переопределять операторы языка РуФоп Теперь давайте рассмотрим третье основное отличие классов и модулей: перегрузку операторов. Говоря простым языком, перегрузка операторов позволяет объектам, созданным из классов, перехватывать и участвовать в операциях, которые применяются к встроенным типам: сложение, получение среза, вывод, дополнение имен и т. д.
По большей части это автоматический механизм: при выполнении выражений и других встроенных операций интерпретатор передает управление реализации классов. В модулях нет ничего подобного: модули могут реализовать функции, но не операторы выражений. Мы можем полностью реализовать поведение класса в виде методов, однако перегрузка операторов позволяет объектам теснее интегрироваться в объектную модель языка Рус)1оп.
Кроме того, перегрузка операторов помогает нашим объектам действовать так же, как действуют встроенные объекты, потому что она позволяет создавать менее противоречивые и более простые в изучении интерфейсы объектов и обеспечивает возможность обрабатывать объекты, созданные из классов, программным кодом, который предполагает взаимодействие со встроенными типами. ° Имена методов, начинающиеся и заканчивающиеся двумя символами подчеркивания ( Х ), имеют специальное назначение. Перегрузка операторов в языке Руспоп реализуется за счет создания методов со специальными именами для перехватывания операций.
Язык Руспоп определяет фиксированные и неизменяемые имена методов для каждой из операций. ° Такие методы вызываются автоматически, когда экземпляр участвует во встроенных операциях. Например, если объект экземпляра наследует метод вбб, этот метод будет вызываться всякий раз, когда объект будет появляться в операции сложения (ь). Возвращаемое значение метода становится результатом соответствующей операции.
° Классы могут переопределять большинство встроенных операторов. Существует множество специальных имен методов для реализации перегрузки почти всех операторов, доступных для встроенных типов. Сюда входят операторы выражений, а также такие базовые операции, как вывод и создание объекта. ° В методах перегрузки операторов не существует аргументов со значениями по умолчанию, и ни один из таких методов не является обязательным для реализации. Если класс не определяет и не наследует методы перегрузки операторов, это означает лишь то, что экземпляры класса не поддерживают эти операции. Например, если отсутствует метод зоо, попытка выполнить операцию сложе.
ния ь будет приводить к возбуждению исключения. Классы могут переопределять операторы языка Руйоп ° Операторы позволяют интегрировать классы в объектную модель языка Руотси. Благодаря перегрузке операторов объекты, реализованные на базе классов, действуют подобно встроенным типам и тем самым обеспечивают непротиворечивые и совместимые интерфейсы. Перегрузка операторов является необязательной функциональной особенностью — она используется в основном специалистами, создающими инструментальные средства для других программистов, а не разработчиками прикладных программ. И честно говоря, вам не стоит использовать ее только потому, что это «круто».
Если не требуется, чтобы класс имитировал поведение встроенных типов, лучше ограничиться использованием простых методов. Например, зачем приложению, работающему с базой данных служащих, поддержка таких операторов, как * и»? Методы с обычными именами, такими как С(чейз!ее и ргоаоте, обычно более уместны.
Вследствие этого мы не будем в этой книге углубляться в подробности реализации методов перегрузки каждого оператора, доступного в языке РуЫтоп. Однако имеется один метод перегрузки оператора, который можно встретить практически в любом классе: метод тптт, который известен как конструктор и используется для инициализации состояния объектов. Методу Тл(т следует уделить особое внимание, потому что он, наряду с аргументом зе11, является одним из ключей к пониманию ООП в языке РуЫтоп.
Третий пример »> о1азз ТП1гбс1авз(эеоопбс1азз). бе1 1п11 (зе11, ча1ие): зе11.бата = ча1ие бе1 абб (зе11, отпег): гати гп тп(гбс1азз(зе11. бата + бег ви1 (зе11, отлет): зе11.бата = вв11.бата ° отлет Я Наследует ЯесолОС!азз я Вызов "ТШ гОС!азз(ча!ие)" я для еырэыения "зе! Г « огпег" отлет) я для выраыения "зе!Г * отпег" »> в = ТЬ1гбС1азз("аЬо") »> в. б1зр1ау() Сиггепт ча1ие = "аЬс" й Вызывается новый иетод тлтг я унаоледовенный иетод я новый зоо : создэетоя новый экземпляр »> Ь = а + 'хуа' »> Ь.б1вр1ву() Сиггепт ча1ие = "аоскуп' На этот раз мы определим подкласс от БесопбС1азз, реализующий три специальных метода, которые будут вызываться интерпретатором автоматически: метод !п11 вызывается, когда создается новый объект экземпляра (аргумент зе11 представляет новый объект Тот гбС1азз), и методы абб и эи1 вызываются, когда экземпляр ТлтгбС1азз участвует в операциях + или *, соответственно.
Ниже приводится определение нашего класса: 589 Самый простой в мире класс на языке Рут)топ жете интерпретировать операторы в своих объектах классов,как вам будет угодно.' С другой стороны, вы могли бы использовать перегрузку операторов, чтобы иметь возможность передавать объекты, определяемые пользователем, в функцию, которая выполняет операции, поддерживаемые встроенными типами, такими как списки или словари. Наличие реализации того же самого набора операторов в вашем классе обеспечит поддержку вашими объектами тех же самых интерфейсов и, как следствие, совместимость с используемой функцией. Один из методов перегрузки присутствует практически во всех реалистичных классах: метод-конструктор штт .
Он позволяет классам немедленно заполнять атрибуты вновь созданных экземпляров, поэтому конструктор полезно использовать практически во всех разновидностях ваших классов. Фактически даже при том, что в языке РуЫтоп атрибуты не объявляются, вы без труда сможете определить, какие атрибуты принадлежат экземплярам, просмотрев программный код метода 1лы В этой книге мы не будем углубляться в изучение этой расширенной особенности, но мы еще будем рассматривать некоторые приемы использования механизма наследования и перегрузки операторов в главе 24. Самый простой в мире класс на языке РуФоп В этой главе мы приступили к детальному изучению синтаксиса инструкции с1азэ, но я еще раз хотел бы напомнить вам, что сама модель наследования, на которой основаны классы, очень проста — суть ее заключается всего лишь в организации поиска атрибутов в деревьях взаимосвязанных объектов.
Фактически мы можем создавать вообще пустые классы. Следующая инструкция создает класс без присоединенных к нему атрибутов (объект пустого пространства имен): »> с1авв тес: савв р Обьект пустого пространства имен Инструкция пустой операции раза (обсуждавшаяся в главе 13) необходима потому, что здесь у нас нет никаких методов с программным кодом.
После создания класса, выполнив инструкцию в интерактивной оболочке, мы можем приступать к присоединению атрибутов, выполняя операции присваивания из-за пределов класса: а Так ке дпп обьектоа с атрибутами »> гес.паве = 'Воэ' »> тес.аое = 40 Но лучше этого не делать.