Лутц М. - Изучаем Python (1077325), страница 128
Текст из файла (страница 128)
Повторюсгы возможно создавать атрибуты с помощью операции присваивания в любом месте программы, где доступна ссылка на экземпляр, даже за пределами инструкции с1азз. Обычно все атрибуты экземпляров инициализируются в конструкторе 1п11 — благодаря этому при последующих вызовах методов можно быть уверенным в существовании необходимых атрибутов. зе1г — это имя, которое обычно дается первому (самому левому) аргументу в методах классов. Интерпретатор РуФЬоп автоматически записывает в него объект экземпляра, который подразумевается при вызове метода. Этот аргумент не обязательно должен носить имя эе1г, главное — это положение аргумента. (Бывшие программисты на С++ или дача могут предпочесть использовать имя ГЛ1з, потому что в этих языках программирования это имя является отра- 594 Глава 23. Основы программирования классов жением той же идеи, только в языке РуФЬоп этот аргумент всегда должен присутствовать явно.) 6.
Перегрузка операторов в языке РуСЬоп выполняется с помощью методов со специальными именами — онн начинаются и заканчиваются двумя символами подчеркивания. Эти имена не являются встроенными или зарезервированными именами — интерпретатор РуФЬоп просто автоматически вызывает методы с этими именами, когда экземпляр появляется в составе соответствующей операции. Язык РуФпоп определяет порядок отображения операций на специальные имена методов. 7. Перегрузка операторов может использоваться для реализации объектов, которые имитируют поведение встроенных типов (например, последовательностей или числовых объектов, таких как матрицы), и для реализации интерфейсов встроенных типов, которые ожидают получить те или иные части программного кода.
Кроме того, имитация интерфейсов встроенных типов позволяет передавать в экземплярах классов информацию о состоянии — то есть атрибуты, в которых сохраняются данные между вызовами операций. Однако не следует использовать перегрузку операторов, когда достаточно использовать простые методы. 8. Наиболее часто используется метод конструктора гп11 — этот метод присутствует практически во всех классах и используется для установки начальных значений атрибутов экземпляров и выполнения других начальных операций.
9. Наиболее важными концепциями ООП в языке РуФ)1оп являются аргумент эе1г в методах и конструктор 1п11 Подробнее о программировании классов Если что-то в главе 23 осталось для вас непонятным, не волнуйтесь — теперь, совершив краткий тур, мы начнем копать немного глубже и подробно изучим понятия, представленные ранее. В этой главе мы с другой стороны посмотрим на классы и методы, наследование и перегрузку операторов, формализуем и дополним некоторые идеи программирования классов, представленные в главе 23. Поскольку классы являются нашим последним инструментом пространств имен, то здесь мы также сведем вместе концепции пространств имен в языке Ру1поп.
Кроме того, в этой главе будут представлены примеры более крупных и более реалистичных классов, чем те, что мы видели до сих пор, в том числе завершающий пример, который связывает воедино многое из того, что мы узнали об ООП. Инструкция с1аы Несмотря на то что на первый взгляд инструкция с1ааа в языке РуФЬоп напоминает похожие инструменты в других языках программирования, при более близком рассмотрении видно, что она существенно отличается от того, к чему привыкли некоторые программисты.
Например, как и инструкция с1ааа в языке С++, инструкция с1ааа в языке Ру1Ьоп является основным инструментом ООП, но в отличие от инструкции в С++, в языке РуФЬоп она не является объявлением. Подобно инструкции Се1, инструкция с1ааа создает объект и является неявной инструкцией присваивания — когда она выполняется, создается объект класса, ссылка на который сохраняется в имени, использованном в заголовке инструкции. Кроме того, как и инструкция сеГ, инструкция с1ааа является настоящим выполняемым программным кодом— класс не существует, пока поток выполнения не достигнет инструкции 59б Глава 24. Подробнее о программировании классов с1авв, которая определяет его (обычно при импортировании модуля, в котором она находится, но не ранее).
Общая форма Инструкция с1авв — это составная инструкция, с блоком операторов, обычно под строкой заголовка. В заголовке после имени в круглых скобках через запятую перечисляются суперклассы. Наличие более одного суперкласса в списке означает множественное наследование (которое будет обсуждаться в следующей главе). Ниже показана общая форма инструкции: с1авв <паве>(вврегс1авв.. ): бата = уа1ие Ое( ае(лое(ве1Г, ве1Г аеавег = ыа1ое В Присваивание имени в Совместно используемые данные класса в Методы В Данные экземпляров Внутри инструкции с1авв любая операция присваивания создает атрибут класса, а методы со специальными именами перегружают операторы. Например, функция с именем тп11, если она определена, вызывается во время создания объекта экземпляра.
Пример Как мы уже видели, классы — это всего лишь пространства имен, то есть инструменты, определяющие имена (атрибуты), с помощью которых клиентам экспортируются данные и логика. Так как же инструкция с1аав порождает пространство имен2 А вот как. Так же как и в модулях, инструкции, вложенные в тело инструкции с1авв, создают атрибуты класса. Когда интерпретатор достигает инструкции с1авв (а не тогда, когда происходит вызов класса), он выполняет все инструкции в ее теле от начала и до конца. Все присваивания, которые производятся в течение этого процесса, создают имена в локальной области видимости класса, которые становятся атрибутами объекта класса. Благодаря этому классы напоминают модули и функции: ° Подобно функциям, инструкции с1авв являются локальными областями видимости, где располагаются имена, созданные вложенными операциями присваивания.
° Подобно именам в модуле, имена, созданные внутри инструкции с1авв, становятся атрибутами объекта класса. Основное отличие классов состоит в том, что их пространства имен также составляют основу механизма наследования в языке РуьЬоп— ссылки на атрибут, отсутствующие в классе или в объекте экземпляра, будут получены из других классов. Поскольку инструкция с1авв — это составная инструкция, в ее тело могут быть вложены любые инструкции — рг(пг, =, 1г, Се( и т.
д. Все инст- 597 Инструкция с!азз рукции внутри инструкции с1авз выполняются, когда выполняется сама инструкция с1авв (а не когда позднее класс вызывается для создания экземпляра). Операции присваивания именам внутри инструкции с1азз создают атрибуты класса, а вложенные инструкции бе1' создают методы класса; кроме этого, атрибуты класса создаются и другими инструкциями, выполняющими присваивание.
Например, присваивание объекта, не являющегося функцией, атрибутам создает атрибуты данных, совместно используемых всеми экземплярами: »> с1ввв Зввгвбрвтв; эрве = 42 в Создает атрибут данник класса »> х = Зввгебратв() »> у = Зввгебрвтв() »> х.арве, у,арве в Создать два зкзеилляра в Они наследуют и совиестно используют в атрибут зрав (42, 42) В данном случае из-за того, что имя зрав создается на верхнем уровне в инструкции с1азз, оно присоединяется к классу и поэтому совместно используется всеми экземплярами. Мы можем изменять значение атрибута, выполняя присваивание через имя класса и обращаться к нему через имена экземпляров или класса.' »> Зэвгебрвта.арве = 99 »> х.врвв, у.арве, Зьвгвбрвтв.арве (99, 99, 99) Такие атрибуты класса могут использоваться для хранения информации, доступной всем экземплярам, например для хранения счетчика количества созданных экземпляров (эту идею мы рассмотрим в главе 26).
Теперь посмотрим, что произойдет, если присвоить значение атрибуту зрзв не через имя класса, а через имя экземпляра: »> х,арве = 88 »> х,вове, у,врвв, зэагвбрвтв.арве (88, 99, 99) Если у ввс есть опыт работы с языком С++, вы можете заметить в этом некоторое сходство со «статическими» членами данных в языке С++ — членамн, которые хранятся в классе независимо от экземпляров. В языке Русбон в атом нет ничего особенного: все атрибуты класса — это всего лишь имена, созданные в инструкции с1ввв, независимо от того, ссылаются онн нв методы («мстоды» в С++) нлн нв что-то другое (<члены» в С++).
Операция присваивания, применяемая к атрибуту экземпляра, создает или изменяет имя в экземпляре, а не в классе. Вообще говоря, поиск в дереве наследования производится только при попытке чтения атрибута, но не при присваивании: операция присваивания атрибуту 598 Глава 24. Подробнее о программировании классов объекта всегда изменяет сам объект, а не что-то другое.' Например, ат- рибут у, враз будет найден в наследуемом классе, а операция присваи- вания атрибуту х. враз присоединит имя непосредственно к объекту х.
Ниже приводится более понятный пример этого поведения, где одно и то же имя создается в двух местах. Предположим, что мы использу- ем следующий класс: с1авв М!хебнваев: ба!в = 'враз' бе( !п!! (ве11, ча1ое); ве1С бага = ча1ое бег б!вр1ау(ве!Г): рг!п! ве1(.ба!а, Мьхебхаеев.ба!а а Определение класса В Присваиваиие атрибуту класса а Присваиваиие ииеии метода В Присваиваиие атрибуту экземпляра а Атрибут зкзеипляра, в атрибут класса Этот класс содержит две инструкции бег, которые связывают атрибуты класса с методами.