Лутц М. - Изучаем Python (1077325), страница 145
Текст из файла (страница 145)
Свойства (и слоты) основаны на новом понятии дескрипторов атрибутов — темы слишком сложной, чтобы обсуждать ее здесь. Проще говоря, свойства — это тип объектов, который присваивается именам атрибутов класса. Они создаются вызовом встроенной функции ргорвгту с тремя методами (обработчиками операций получения, присваивания и удаления) и строкой документирования — если в каком-либо аргументе передается значение Нэпе, следовательно, зта операция не поддерживается. Определение свойств обычно производится на верхнем уровне в инструкции 01авв (например, паев = ргорегту(... )). Когда выполняется такое присваивание, при попытке доступа к атрибуту класса (то есть, оЬ).
паве) автоматически будет вызываться один из методов доступа. Например, метод ретаттг позволяет классам перехватывать попытки доступа к неопределенным атрибутам класса: »> с1ааа с1авв1с: Сат вататтг (ва1Г, пава): б75 Классы нового стиля 11 паве == 'вцв", ге1огп 40 в1зв: га1вв А11г1Ьо1еЕггог »> х = о1азв1о() »> х.аце 40 »> х.паве АыгтьосеЕггог № Запустит метод дегагтг № Запустит иетод детасгг Ниже тот же пример, но уже с использованием свойств: »> о1азв пемргорз(ЬЬ) ео1): Пеу цесаре(зе)Г): ге1огп 40 аце = ргорвгсу(цесаре, иоле, иоле, Иоле) № дег, зес, де), оосз »> х = пемргорз() »> х.аце № Запустит метод десзде 40 »> х.паев № Нормальная операция извлечения Атсыьотееггог: пемргорз тпзсапсе паз по а11гтьоте 'паве' (А11г1ЬосеЕггог.
экземпляр пеиргорз не имеет атрибута 'паве') В некоторых случаях свойства могут быть менее сложными и работать быстрее, чем при использовании традиционных подходов. Например, когда добавляется поддержка операции присваивания атрибуту, свойства становятся более привлекательными — программный код выглядит компактнее и в операцию присваивания не вовлекаются дополнительные вызовы методов, если не требуется производить дополнительных вычислений: »> о1азв пемргорз(оо)ео1): Веу цесаре(ве11); ге1огп 40 Веу зесаце(вв11, ча1ое); рг1п1 'ве1 аце;', чв1ое ве11.
ацв = ча1ое . аце = ргорег1у(це1аце, ве1ацв, Иоле, Иоле) »> х = пемргорв() »> х,аце 40 »> х.вцв = 42 зе1 аце: 42 »> х, аце 42 »> х.)оо = '1га1пег' зегаде »> х.)оЬ 'сгатпег' № Запустит метод де(аде № Запустит метод зесаде № нормальная операция извлечения; нет вызова де(аде № Нормальная операция присваивания; нет вызова № нормальная операция извлечения; нет вызова де(аде 676 При эквивалентном классическом решении проблемы класс мог бы производить лишние вызовы метода и, возможно, выполнять присваи- вание значения атрибуту с использованием словаря, чтобы избежать зацикливания: »> с)ааа с)ааа1с: бег да1а11г (ае)г, паве); а При сснлке нз неопределенний атрибут (Г паве == 'аде': гатогп 40 а)ае: га1ае А11г1оцтеЕггог бег ае1а11г (ае)г, паза, ча)це): 4 для всех операций присваивания рг1пт 'ае1: ', паве, ча)це 11 паве == 'аде'.
аа)Г, б(с1 [' аде') = ча)оа е)ае: ае)Г. б1с1 [паве) = ча)се д Запустит метод детаттг а Запустит иетод зетатгг Ф Определен: нет сизова детаыг я Запустит иетод зегамг опять я Определен нет яизояа дзтаыт Для этого примера свойства обладают неоспоримым преимуществом. Однако в некоторых приложениях методы деса11г и зетастг по- прежнему могут быть востребованы для обеспечения более динамичных или универсальных интерфейсов, чем можно реализовать с помощью свойств. Например, во многих случаях невозможно заранее определить набор поддерживаемых атрибутов, которые могут даже не существовать вообще в каком-либо виде на момент написания класса (например, при делегировании ссылок на произвольные методы в обернутых( встроенных объектах).
В таких случаях использование более универсальных методов обслуживания атрибутов дета11г и зе1аттг, которым передаются имена атрибутов, может оказаться предпочтительнее. Кроме того, простейшие ситуации могут обслуживаться этими обработчиками, поэтому свойства следует рассматривать как дополнительное и необязательное к использованию расширение. Новый метод перегруэки детатег)ЬоСе Метод дета11г1Ь01е имеется только в классах нового стиля и позволяет классам перехватывать все попытки обращения к атрибутам, а не только к неопределенным (как метод детаттг ). Кроме того, этот метод более сложен в обращении, чем,детзгтг и зетаттг (из-за более высокой вероятности зацикливания).
Полное описание этого метода я оставляю за стандартной документацией по языку Рус)топ. »> х = с)ааа1с() »> х.ада 40 »> х,аде = 41 зет: аде 41 »> х. аде 41 »> х.)ЬЬ = '1га1пег' »> х.)ЬЬ Глава 2б. Дополнительные возможности классов 677 Статические методы и методы класса Помимо всех этих особенностей классы нового стиля интегрируются с понятием расширения типов, упоминавшимся выше в этой главе,— возможность расширения типов и классы нового стиля были введены вместе с разделением тип/класс в версии РуФ)топ 2.2 и выше.
Поскольку особенности классов нового стиля — это достаточно сложные темы, мы не будем углубляться в подробности в этой книге. За дополнительной информацией обращайтесь к документации к выпуску РуФ)топ 2.2 и справочным руководствам по языку. Статические методы и методы класса До выхода версии РуЗ)топ 2.2 методы класса никогда нельзя было вызывать без передачи экземпляра в качестве аргумента. В РуЗ)топ 2.2 и более поздних версиях такое поведение также используется по умолчанию, но существует возможность изменить такое положение дел с помощью новой дополнительной особенности, получившей название статические методы — простые функции без аргумента ве11, вложенные в определение класса и предназначенные для работы с атрибутами класса, а не экземпляра.
Такие методы обычно используются для обработки информации, которая имеет отношение ко всем экземплярам (например, число созданных экземпляров), а не для реализации поведения экземпляров. В предыдущей главе мы говорили о несвязанных методах: когда мы извлекаем функцию по имени класса (а не экземпляра), мы получаем объект несвязанного метода. Хотя объекты несвязанных методов определяются с помощью инструкции бег, они не являются простыми функциями — они не могут вызываться без передачи им экземпляра класса. Например, предположим, что необходимо использовать атрибуты класса для подсчета числа экземпляров, созданных из класса (как показано в следующем файле врат ру).
Не забывайте, что атрибуты класса совместно используются всеми экземплярами, поэтому мы можем хранить счетчик непосредственно в объекте класса: с1авв Зраа: пов1пвтапсев = 0 оаг ттт (ве1(): Зраа,пов1птапсев = Зрав.пов1пвтапсев а т оег ргтптмоа1пвтапсев(): рг!пт "мовьаг ог (пвтапсев сгеатео: ", зрав.поа1пвтапсев Но такая реализация не будет работать — метод ргтптзов1пвтапсев по- прежнему ожидает получить экземпляр при вызове, потому что функция ассоциирована с классом (даже при том, что в заголовке инструкции оет отсутствуют какие-либо аргументы): >» гсов враз 1врогт >» а = Зрав() >» Ь = Зрав() »> о Зрав() 678 Глава 26, Дополнительные возмомсности классов »> Враз.рг1пвйоа1пвзвпсвв() тгасеьаск (тппегаозт 1азт).
ЕТ1е т<зтотп>", 11пе т, 1п ? турееггог: опьоопс аетьос моет ье са11ео м!аь с1авз тпвтапсе Гм агроаепт (ТуреЕггог; несвязаннмй метод должен вмзмваться с экземпляром класса в 1-м аргументе) Проблема состоит в том, что несвязанные методы экземпляра — это ие то же самое, что простые функции. Эта проблема главным образом является проблемой знания особенностей языка.
Самая простая мысль, которая приходит в голову, — сделать метод обычной функцией, а ие методом класса. При таком способе функции не требуется передавать экземпляр класса: Оет рг1птлоа!пвтапсев(): рппт "номьег ог тпзсапсев сгеатео: ", Брав.поа1пзтапсез с1авв Брам: пом1пвтапсев = О Сет тп(т (зе1Г): Брам поа1пзтапсев = Брам.поа1пвтапсез + т »> 1арогт враз »> а = враз.враз() »> Ь = враз,враз() »> с = враз.враз() »> враз.рг1птйоа1пвсапсев() НомЬег от тпв(апсев сгеатео: 3 »> враз.враз.поа1павапсев 3 Поскольку имя класса доступио простой функции в виде глобальной переменной, все работает прекрасио.
Кроме того, обратите внимание, что имя самой функции также является глобальным, ио только в этом единственном модуле — оио ие будет конфликтовать с именами в других модулях программы. Мы могли бы сделать то же самое, вызывая функцию через экземпляр, как обычно, хотя это не очень удобно, особенно если создание экземпляра приводит к изменениям в данных класса: С1дзв Брдм: поа1пвтапсев = О Оет 1пм (ве!Г): Брам поа1пыапсев = Брам.поа1пвтапсев + ) Оет рыптлом1пвтапсез(ве1Г): рг(ы Гвоаьег ог тпзтапсев сгеатео: ", Брам.поа1пвтапсев »> Ггоа враз 1арогт Враз »> а, Ь, с = Враз(), Брав(), Брав() »> а.рг1птзоа1пвтапсез() Лоаьег от (пзтапсев сгеатес 3 »> ь.рг1птаоа1пвтапсев() Лоаьег от тпвтапсев сгеатес 3 679 Статические методы и методы класса х» Зраа().рг!Итйоа1пвтапоевп поаоег от тпвтапсев сгеатес: 4 До появления статических методов в РуФЬоп 2.2 некоторые теоретики языка утверждали, что в языке Ру!Ьоп отсутствуют методы класса— только методы экземпляра.