Лутц М. - Изучаем Python (1077325), страница 132
Текст из файла (страница 132)
Как и прежде, подобных результатов можно было бы достичь с использованием встроенных инструментов, например с помощью операции получения среза с третьим граничным значением, чтобы организовать пропуск элементов: »> 3 = 'аЬсбет' »> Гог х 1п 3[;:2В Гог у 1п 3[::2): рг1пт х + у, й Новые объекты в каидой итерации аа ас ае са сс се еа ес ее »> 3 = 'аЬсбеГ' »> 3 = 3[::2) Однако это далеко не то же самое по двум причинам. Во-первых, каждое выражение извлечения среза физически сохраняет весь список с результатами в памяти, тогда как итераторы воспроизводят по одному значению за раз, что позволяет существенно экономить память в случае большого объема результатов.
Во-вторых, операции извлечения среза создают новые объекты, поэтому в действительности итерации не протекают одновременно в одном и том же объекте. Чтобы оказаться ближе к реализации на основе классов, нам необходимо было бы создать единственный объект для обхода, заранее выполнив операцию извлечения среза: 615 Перегрузка операторов >» з 'асе »> 1сг х 1п 8: гог у 1п В: Ф Тот ие сания обьект, ноаие итератпры рг1пт х + у, аа ас аа са сс се еа ес ее Эта реализация больше похожа на наше решение, выполненное с помощью классов, но здесь по-прежнему список с результатами целиком хранится в памяти (на сегодняшний день не существует генераторов, способных формировать срезы), и эта реализация эквивалентна только для данного конкретного случая пропуска каждого второго элемента.
Итераторы могут выполнять любые действия, которые можно реализовать в классах, поэтому они обладают более широкими возможностями, чем предполагается в данном примере. Независимо от того, требуется ли такая широта возможностей в наших приложениях, итераторы, определяемые пользователем, представляют собой мощный инструмент — они позволяют создавать произвольные объекты, которые выглядят и ведут себя подобно другим последовательностям и итерируемым объектам, с которыми мы встречались в этой книге. Мы могли бы использовать этот механизм, например, для создания объекта базы данных, чтобы одновременно выполнять несколько итераций в одном и том же наборе данных, извлеченном в результате запроса к базе данных.
Яе1аттг и 5етаттг перехватывают обращения к атрибутам Метод пега(гг выполняет операцию получения ссылки на атрибут, Если говорить более определенно, он вызывается с именем атрибута ввиде строки всякий раз. когда обнаруживается попытка получить ссылку на неопределенный (несуществующий) атрибут. Этот метод не вызывается, если интерпретатор может обнаружить атрибут посредством выполнения процедуры поиска в дереве наследования. Вследствие этого метод Ое(а(1г удобно использовать для универсальной обработки запросов к атрибутам.
Например: »> с1аав аавсу се1 да(а11г (вв11, астгпааа): 11 а11гпава == "аса": гатвгп 40 а1зе: га1ае Агтг(ацтеЕггпг, аттгпааа »> Х = ааргу() »> Х.ара 40 »> Х.пава Глава 24, Подробнее о программировании классов 61б ... текст сообшении об ошибке опушен.. А11МЬчтеЕггог: паве В этом примере класс и его экземпляр Х не имеют своих собственных атрибутов, поэтому при обращении к атрибуту Х, аде вызывается метод де1а11г — в аргументе Ве1( передается экземпляр (Х), а в аргументе агггпаше — строка с именем неопределенного атрибута ("аде").
Класс выглядит так, как если бы он действительно имел атрибут аде, возвращая результат обращения к имени Х. аде (40).В результате получается атрибут, вычисляемый динамически. »> с1авв ассвввсоптго1: бет ве1а11г (ве11, а11г, ча1ое): 11 а11г == 'аое". ве11. 41с1 (а11г) = ча1ов е1ве; гв1ве А11г1ЬотеЕггог, в11г + ' по1 аыошеб' »> Х = ассеввсопкго1() »> Х.аде = 40 »> Х.аде 40 »> Х,паве = 'ве1' текст сообщении об ошибке опушен А11гтоотеЕггог: паше по1 а11оиее Ф Вызовет метод зегзыг Эти два метода перегрузки операций доступа к атрибутам позволяют контролировать или специализировать доступ к атрибутам в ваших объектах.
Они могут играть весьма специфические роли, часть из ко- торых мы рассмотрим далее в этой книге. Для атрибутов, обработка которых классом не предусматривается, возбуждается встроенное исключение Я11г1ЬЬ1еЕггсг, чтобы сообщить интерпретатору, что это действительно неопределенные имена, — попытка обращения к имени Х. папе приводит к появлению ошибки. Вы еще раз встретитесь с методом де1а11г, когда мы будем рассматривать делегирование и свойства в действии в следующих двух главах, а об исключениях я подробно буду рассказывать в седьмой части книги. Родственный ему метод перегрузки вега11г перехватывает все попытки присваивания значений атрибутам.
Если этот метод определен, выражение ве1(.а11г = ча1ое будет преобразовано в вызов метода ве1(. Вегас(г ('а11г', ча)ое). Работать с этим методом немного сложнее, потому что любая попытка выполнить присваивание любому атрибуту аргумента ве1( приводит к повторному вызову метода ве1а11г вызывая бесконечный цикл рекурсивных вызовов (и, в конечном итоге, исключение переполнения стека1). Если вам потребуется использовать этот метод, все присваивания в нем придется выполнять посредством словаря атрибутов, как описывается в следующем разделе.
Используйтезе11, 0101 (' паве' ) = х,аневе1(.паше = х: 617 Перегрузка операторов Имитация частных атрибутов экземпляра Следующий фрагмент является обобщением предыдущего примера и позволяет каждому подклассу иметь свой перечень частных имен ат- рибутов, которым нельзя присваивать значения: № Подробнее об исключениях позднее с1авв РгтчатеЕхс(Ехсерттоп), рава с1авв Ргтчасу. оег ветаттг (ве1(, аттгпаае, ча1ое): тг аттгпаае тп ве1(.рг!чатев: гатов РгтчатеЕхс(аттгпаае, зе1() е1ве: ве1(, о!с! [амгпаае] = ча1ое № Внзиезет зв1(,ат(гпаае = ча1ое № Бе1 Г.амглаае = ча!ое № зазовет зацикливание' с1авз Тевтт(Рычасу) ргтча:ев = ['аде ] с1авв Теж2(Ргтчасу): рыча!ее = ['паве', 'рау ] оег тпы (ве1(): ве1( отст ['папе ] = 'тоа' х = Твзтт() у = Теж2() х.папе = 'Вос' У.паве = 'Вое' № <== ошибка У.аде = 30 х.аде = 40 № <== оюибка Перехват операций обращения к атрибутам и присваивания им значений — вообще очень полезный прием.
Он обеспечивает возможность делегирования — способ, позволяющий обертывать встроенные объекты объектами-контроллерами, добавлять новое поведение и делегировать Фактически это лишь первая прикидочная реализация частных атрибутов в языке РуЕ[топ (то есть запрет на изменение атрибутов вне класса). Несмотря на то что язык Ру(]топ не поддерживает возможность объявления частных атрибутов, такие приемы, как этот, могут их имитировать.
Однако это лишь половинчатое решение — чтобы сделать его более эффективным, его необходимо дополнить возможностью изменять значения частных атрибутов из подклассов и использовать метод десаттг и класс-обертку (иногда называется прокоп-классом), чтобы контролировать получение значений частных атрибутов. Полную реализацию я оставлю как упражнение для самостоятельного решения, потому что хотя таким способом можно имитировать сокрытие атрибутов, это практически никогда не используется.
Программисты, использующие язык Ру[]топ, способны писать крупные объектноориентированные платформы без частных объявлений, но существующие интересные решения по управлению доступом далеко выходят за рамки нашего обсуждения. 618 Глава 24. Подробнее о программировании классов выполнение операций обернутым объектам (подробнее о делегирова- нии и классах-обертках рассказывается в следующей главе). герг и з1г возвращают строковое представление В следующем примере реализованы конструктор 1п!1 и метод перегрузки вбб, которые мы уже видели, но также в нем реализован метод герт , который возвращает строковое представление экземпляров. Здесь этот метод используется для преобразования объекта зе)П ба1в в строку. Если метод герт, (или родственный ему метод з1 г ) определен, он автоматически будет вызываться при попытках вывести экземпляр класса или преобразовать его в строку.
Эти методы позволяют определить более удобочитаемый формат вывода ваших объектов: »> с1азз аббег; бе( 1п11 (зв11, ча1ыв=с): ве1(.ба1а = ча1ые бе№ абб (зе11, о1вег); зе)т.баса += осввг № Инициализировать атрибут бата № Прибавить другое значение »> с1авв аббгврг(аббат): бет гврг (зе11): ге1игп 'аббгврг(Хз)' »> х = аббгврг(2) »> х+1 »> х апбгерг(3) »> рг1п1 х аббгерг(3) »> зсг(х), герт(х) ('аббгерг(3)'. 'аббгерг(3)') № Вызывает герт № Вызывает герт Почему имеется два метода вывода т Дело в том, что сначала выполняется попытка использовать метод зсг, чтобы вывести объект в удобном для пользователя виде, как это делают инструкция р г тот и встроенная функция з(г.
Метод герт должен возвращать строку, которая могла бы использоваться как программный код для воссоздания объекта, — он используется при автоматическом выводе результатов выражений в интерактивной оболочке и в функции герт. Если метод згг отсутствует, интерпретатор использует метод герт (но не наоборот): »> с1авз аббз1г(аббат); бвг в1г (ве11): № есть зтг, но нет метода герт ге1егп '(ча1ов: хз)' х зв1№,ба1в № преобразовать в красивую строку № По умолчанию вызывается герт ат ОхООВЗВЕЕО> № Вызывает зтг »> х = аббвсг(3) »> х+1 »> х < ва1п .аббз1г 1пзгапсе »> рг1п1 х № Наследует тлтт , адд № Добавляет строковое представление х зе1№.ба1а № преобразует в строку № программного кода № Вызывает злтт № Вызывает абб № Вызывает герт Перегрузка операторов б19 [Ча!ое: 4] »> зсг(х), герт(х) ( '[ча1ие: 4]', '< васи .аббзсг спзсапсе ас ОхООВ35ееО>') »> о)ввз вббьоСП(аббе г): бег зтг (зе11): гесогп '[уа1ое; Хз]' Х зв11.баса бе1 герт (зе)1): геСигп 'аббЬоСЬ(Хз)' Х зеи.бата № Удобоиитаеизя строка № Строка яраграиииого кода »> х = аббЬоСП(4) »>х+т >» х аббооСП(5) »> рг1пт х [Ча1че 5] »> зСг(х), герт(х) ( [ча)ое: 5]', 'аббьосп(5) ) № Вызывает герт № Вызывает згг Похоже, что метод аС г (и его низкоуровневый родственник герт ) является вторым по частоте использования после 1п1с среди методов перегрузки операторов в сценариях на языке РуС]топ — всякий раз, когда вам приходится видеть адаптированное отображение при выводе объекта„это значит, что скорее всего был использован один из этих методов.