Лутц М. - Изучаем Python (1077325), страница 135
Текст из файла (страница 135)
в!с! ('вата!'1 ( ардв, ардв ) »> Х.ааааа = 'тоавт' »> Х. В!ст ('Вата1' 'арав', 'за!аз': 'тоаат', 'вата2'; 'в99а ) »> Х. В!ст ( за!аз' 1 = 'Вав' »> Х.за!аз 'аав' Однако такая эквивалентность применяется только к атрибутам, фактически присоединенным к экземпляру. Так как обращение по составному имени также вызывает запуск процедуры поиска в дереве наследования, такой способ может обеспечить доступ к атрибутам, которые нельзя получить индексированием словаря. Например, унаследованный атрибут Х, ве11о недоступен через выражение Х.
9!ст ['йе11о'). Наконец, ниже показано, что дает применение функции 9т г, с которой мы встречались в главах 4 и 14, к объектам экземпляров. Эта функция применяется к объектам, имеющим атрибуты: О! г(о()) ест ), напоминает вызов оЬ)ест, ((!с! . кеуа(). Однако обратите внимание, что функция ((т г сортирует свой список и включает в него некоторые системные атрибуты — начиная с версии Ру$)топ 2.2, функция ((т г также автоматически собирает унаследованные атрибуты »> Х. В!ст ('васат 'ааав', 'за!аз', 'вав', 'аата2'; 'е99а') Содержимое словарей атрибутов и результаты вызова функции Втг могут отличаться. Например, т.к.
теперь интерпретатор позволяет встроенным типам классифицировать себя как классы, для встроенных типов функция в! г включает информацию о методах перегрузки операторов. Вообще, имена атрибутов, начинающиеся и завершающиеся двумя символами подчеркивания, являются особенностью интерпретатора. Подклассы типов будут рассматриваться в главе 26. 630 »> Х, бзсз . КеУв() ['баса!', 'ОасаЗ', 'Оа!а2'] >»> 41г(Х) [' Сос ', ' щосо1е ', 'ба!а!', 'Оа!а2', 'Оа!аЗ', 'Пепе', 'По1а'] »> бтг(зиЬ) [' Оос ', ' щосо1е ', 'Пе11о', 'По1а'] »> О1г(зорег) бес ', ' щосо1е ', 'Ье11о'] Поэкспериментируйте самостоятельно с этими специальными атрибутами, чтобы получить представление о том, как в действительности ведется работа с атрибутами.
Даже если вы никогда не будете использовать их в своих программах, понимание того, что пространства имен— это всего лишь обычные словари, поможет лишить покрова таинственности само понятие пространств имен. Ссылки на пространства имен В предыдущем разделе были представленны атрибуты экземпляра и класса с1азз и Ьавев, но не объяснялось, зачем они могут понадобиться.
В двух словах, эти атрибуты позволяют осматривать иерархии наследования в вашем программном коде. Например, их можно использовать для отображения дерева классов на экране, как в следующем примере: й с1аввсгее.ру Ое( с1аввггее(с1в, 1псеж): ргьпт '.'*ьпбепц с1в.
паве Гог ворегс1в!п с1в. Ьавев: й с1аззггее(ворегс1в, ьпоеп1+3) Ф Вывести имя класса Рекурсивный обход всех суперклассов Ф Кащдый суперкласс мощет быть й посещен более од~ого раза Ое( 1пвгапсе!гее(ьпв!): РГ1п! 'Тгее о(', 1пвс с1аввггее(1пвг, с1авв, 3) Ф Показать зкземлляр Ф Взойти к его классу 1( паве == ' щагп ': ве1ггевс() Функция с1авв! гее в этом сценарии является рекурсивной — она выводит имя класса, используя атрибут паее, и затем начинает подъем к суперклассам, вызывая саму себя. Это позволяет функции выполнять обход деревьев классов произвольной формы — в процессе рекур- оег зе)ггев!(): с1авз А: рава с1авв В(А).
рава с1авв С(А); рава с1авв 0(В,С); раза с1азв Е. рава с1авв Е(О,Е): рава зпвсапсеггее(В()) 1пв!апсесгее(Е()) Глава 24. Подробнее о программировании классов 631 Более реалистичный пример сии выполняется подъем по дереву и заканчивается по достижении корневых суперклассов, у которых атрибут завез пуст. Большую часть этого файла занимает программный код самотестирования — если запустить файл как самостоятельный сценарий, он построит пустое дерево классов, создаст в нем два экземпляра и выведет структуры классов, соответствующие им: % рутаоп с1аввсгее.ру тгее от < вата .В таз(ассе ат ОхООАс8438> В А тгее от < еатп .е (патассе ат ОхООАс40АВ> Е 0 В А С А Е Отступы, отмеченные точками, обозначают высоту в дереве классов. Конечно, мы могли бы улучшить формат вывода и даже отобразить дерево в графическом интерфейсе.
Мы можем импортировать эти функции везде, где нам может потребоваться быстро отобразить дерево классов: »> с1авз Евр: рава »> с1авв Регвоп(Еер): рава »> ЬоЬ = Регвап() »> 1ерогс с1аввтгее »> с1аввтгее. 1пвтапсетгее(ЬЬЬ) тгее от < еаю Регвоп >патассе ат ОхООА034ЕВ> ... РЕгЗОп Еер Независимо от того, будете вы создавать и использовать нечто подобное в своей практике или нет, этот пример демонстрирует один из многих способов использования специальных атрибутов, которые создаются внутренними механизмами интерпретатора.
Еще один пример вы увидите в разделе «Множественное наследование«в главе 25, где будет реализован класс общего назначения для получения списка атрибутов. Более реалистичный пример Большая часть примеров, которые мы видели до сих пор, были искусственными и ограниченными и были предназначены, чтобы помочь вам сосредоточить свое внимание на основах. Однако мы закончим эту главу большим примером, в котором воедино собирается многое из то- Глава 24. Подробнее о программировании классов го, что мы уже изучили. Я включил этот пример по большей части как упражнение для самостоятельного изучения — попробуйте внимательно прочитать программный код примера и понять, как разрешаются вызовы методов.
Если говорить двух словах, следующий модуль, регвоп.ру, определяет три класса: ° депеггс01вр1ау — это смешанный класс, который предоставляет универсальный метод вс г; для любого класса, который будет наследовать его, данный метод возвращает строку, содержащую имя класса, из которого был создан данный экземпляр, а также пары «имя = значение» для всех атрибутов в дереве наследования. Для построения списка пар «имя = значение» он использует атрибут пространства имен 01сг . А для определения имени класса используется встроенный атрибут паве атрибута экземпляра с1ввв .
Так как инструкция рг! ш вызывает метод в1г, при выводе всех экземпляров, наследующих поведение этого класса, будет использован собственный формат вывода. Это общий инструмент. ° Регвоп хранит общую информацию о человеке и предоставляет два метода, которые используются для изменения информации о состоянии обьекта. Кроме того, от своего суперкласса он наследует логику вывода в нестандартном формате. Экземпляр класса Регвоп имеет два атрибута и два метода, управляющие этим классом.
° Евр1оуее — это адаптация класса Регвоп, которая наследует извлечение отчества и вывод в нестандартном формате, а также добавляет новый метод, с помощью которого выполняется увеличение зарплаты служащего, н переопределяет операцию изменения возраста (очевидно, служащие стареют быстрее, чем другие люди). Обратите внимание, что конструктор суперкласса вызывается вручную — нам необходимо выполнить версию суперкласса, чтобы заполнить атрибуты паве и аде. По мере изучения программного кода этого модуля вы увидите, что каждый экземпляр имеет свою собственную информацию о состоянии.
Обратите внимание, как используется наследование для адаптации поведения и как используется перегрузка операторов для инициализации и вывода экземпляров: В регвеп.ру с1авв депег1с01вр1ау: пег дагпегА11гв(ве1(). аыгв = '1п' Гаг Кеу 1п ВЕ1Г Е1С1 ассгв += '~гмвГяв1пс % (кеу, ве1(, п1сс [кеу)) ге1егп амгв Пе( вгг (ве)Г) гешгп <вв: вв> % (ве1(. с1авв .
паве . ве1(,да1пегАыгв()) с1ав в Ре своп(6епе г1с01вр1ау ); Более реалистичный пример бЗЗ Сет гп!т (ве1Г, паве, аде) ве1(.паве = паве ве!т.аде = аде Сет >аз!паве(ве1Г): гетсгп ве1(.паве.вр1>т()(-1) Оет ЫгтПОау(ве1Г) ве!т.аде += 1 с1авв Евр1оуее(Регвоп): оег !п11 (ве11, паве, аде, >оь=йопе, рау=О); Регвоп.
>пгт (ве11, паве, аде) ве1(,>оЬ = >оЬ ве1(.рау = рау оег ьтгтпОау(ве)г): ве11.аде += 2 Сет дтнейатве(ве11, регсепт): ве1т.рау *= (1.0 + регсепс) паве == ' ва1п ЬоЬ = Регвоп('ВоЬ Вв!тп'. 40) ргтпт ЬоЬ рг1пт ЬоЬ. 1автйаве() ЬоЬ.Ы гтЬОау() ргтпт ЬоЬ все = евр1оуее('Вие >осев', 44, >оь='сен', рву=100000) рггы все рггм все. 1автйаве() все.ьтгтьОау() все.О>непа!ве(, 10) ргтпт все Чтобы проверить этот программный код, его можно импортировать и создать экземпляры в интерактивной оболочке.
Ниже приводится пример класса Регвоп в действии. Создание экземпляра вызывает метод 1п!т, вызов именованных методов использует или изменяет информацию о состоянии экземпляра (атрибутов), а попытка вывести экземпляр вызывает унаследованный метод всг, что обеспечивает единообразный формат вывода всех атрибутов: »> Ггов регвоп 1врогт Регвоп »> апп = Регвоп('Апп Вв11Ь', 46) »> апп. 1автйаве() 'Вв!1П' »> апп,ьагсьОау() »> апп.аде 46 »> рг1пт апп <Регвоп; аде=46 паве=Апп Ввгтл Глава 24.
Подробнее о программировании классов Наконец, ниже приводятся результаты выполнения программного кода самопроверки (в нижней части файла, вслед за проверкой атрибута лазе ), который создает экземпляры классов Регвоп и Еар1оуее и изменяет каждый из них. Как обычно, программный код самопроверки выполняется, только если файл запускается как самостоятельный сценарий, а не импортируется как библиотечный модуль. Обратите внимание, как класс Езр1оуее наследует реализацию операции форматированного вывода и извлечения отчества, добавляет дополнительную информацию о состоянии, добавляет метод для получения надбавки и адаптирует версию метода увеличения возраста (сотрудники стареют в два раза быстрее!): % руспоп рагвоп.ру <Регвоп: аде=40 паае=Воо Замп > Зппп <Регвоп: аде=41 паза=Вас Зама > <Езр1оуее; !оЬ=Оач рау=100000 аде=44 пазе=цче Запев > Запев <Еар1оуее.
1оо=печ рау=110000.0 аца=46 паве=Зов допев Проследите, как работает программный код в этом примере, чтобы понять, как эти результаты отражают вызовы методов, — в этом примере сведена воедино большая часть идей, составляющих основу ООП. Теперь, когда вы познакомились с классами в языке Руц)гоп, вы наверняка оцените тот факт, что использованные здесь классы — это немно. гим более, чем пакеты функций, которые внедряют и манипулируют встроенными объектами, присоединенными к атрибутам экземпляра в качестве информации о состоянии. Например, когда метод 1авгхаае разбивает строку и извлекает последнюю часть, он просто применяет встроенные операции обработки строки и списка к объекту, управляемому классом.