|
|
|||||||||||||||||||||||||||||
|
Очаровательный Python: Изящество и неловкость Python. Часть 2 (исходники)Источник: IBM developerWorks Россия Дэвид Мерц
В первой статье этой серии разбираются проблемы последовательностей и сравнений. В этом выпуске развитие темы продолжается. В большинстве объектно-ориентированных языков методы и атрибуты - это практически одно и то же, но не совсем. И те, и другие могут принадлежать классу и/или экземпляру класса. Если не думать о деталях реализации, есть одно ключевое различие: методы объекта - это такая штука, которую можно вызывать и тем самым производить действия и вычисления; атрибуты же просто имеют значения, которые можно узнать (и, возможно, изменить). В некоторых языках (в Java, например) на этом различии все и заканчивается: атрибуты есть атрибуты, методы есть методы. В Java особое значение придается инкапсуляции и изоляции данных; таким образом поощряется использование методов вроде "getX" - "setX" для доступа к закрытым данным класса. В психологии Java использование явных вызовов методов сразу же делает возможным случай, в котором при доступе к данным или их изменении могут понадобиться дополнительные расчеты или какие-либо другие действия. Конечно, результатом Java-подхода становится большая подробность кода и иногда кажущиеся странными правила: вместо В связи с этим стоит отметить довольно необычный подход, реализованный в Ruby. В Ruby требования по скрытию данных еще сильнее, чем в Java: все атрибуты обязательно закрыты; прямой доступ к данным объекта невозможен . В то же время в Ruby имеются некоторые синтаксические возможности, благодаря которым вызовы методов выглядят как доступ к атрибутам в других языках. Во-первых, в Ruby скобки при вызове метода необязательны, во-вторых, названия методов могут содержать символы, которые в большинстве языков являются операторами. Так что на Ruby Python - значительно более гибкий язык, чем Java или Ruby, но это оказывается проблемой в той же мере, в какой и достоинством. В Python доступ Старомодный способС давних времен (еще до Python 2.1) в языке был магический метод Листинг 1. Произойдет ли вызов метода?
Доступ к
Использование __slots__В Python 2.2 появился новый механизм создания "защищенных" классов. Нигде не сказано, для чего в действительности предназначается атрибут Листинг 3. __slots__ как гарантия вызова метода
Объявление Листинг 4. Использование .__setattr__ вместе со .__slots__
Метод .__getattribute__()В Python начиная с версии 2.2 есть возможность использовать метод Листинг 5. Возвращаем "настоящее" значение атрибута
Во всех версиях Python ДескрипторыИтак, мы уже насчитали несколько способов заставить атрибуты вести себя как методы. При помощи этих магических методов можно перехватить доступ, запись или удаление для определенных атрибутов. На самом деле при желании для этого можно использовать регулярные выражения или любые другие способы проверки. В принципе можно во время исполнения управлять доступом к любому заданному псевдоатрибуту любым реализуемым в языке способом. Например, можно не просто сравнить имя атрибута с шаблоном, но и проверить, сохранено ли значение этого атрибута во внешней базе данных. Однако в большинстве случаев требуется обеспечить специальное поведение лишь нескольких атрибутов, в то время как операции над остальными должны работать как обычно. При доступе к обычным атрибутам не нужен ни вызов дополнительного кода, ни задержки, связанные с таким вызовом. В таких случаях можно использовать дескрипторы атрибутов или тесно связанные с ними свойства . На самом деле свойства и дескрипторы используют один и тот же внутренний механизм, но способы их определения сильно различаются. И, как легко догадаться, у каждого способа объявления есть свои преимущества и недостатки. Сначала рассмотрим дескрипторы. Основная идея заключается в том, что атрибуту класса назначается экземпляр другого класса специального вида. Этот специальный класс - класс дескриптора - это класс нового типа, имеющий методы Как правило, неизменяемые дескрипторы возвращают вызываемые объекты (callable objects). На самом деле "неизменяемый дескриптор" - это зачастую просто "красивое" название метода; однако метод, который реально будет вызван, может определяться во время исполнения программы. Тут мы делаем шаг в жутковатый мир метаклассов и декораторов, о котором я уже писал в этой рубрике. Конечно, обыкновенный метод тоже может определять реально исполняемый код в зависимости от каких-либо условий, так что введение неисполняемых дескрипторов не производит никаких коренных изменений в концепции метода. В любом случае изменяемые дескрипторы более универсальны, так что я приведу пример их использования. Такие дескрипторы тоже могут возвращать вызываемые объекты - в конце концов, любая функция в Python может возвращать все, что вам вздумается. Но в нашем примере рассматриваются просто данные (и побочные эффекты операций). Предположим, нам просто потребовалось, чтобы какие-то атрибуты выдавали на STDERR сообщения о своем использовании: Листинг 6. Пример изменяемого дескриптора
Класс Листинг 7. Атрибут класса и атрибут экземпляра
Напротив, сам дескриптор всегда принадлежит классу, несмотря на то, что к нему можно получить доступ через экземпляр класса. Вследствие этого происходит обычно нежелательный эффект, из-за которого дескриптор становится уникальным объектом (singleton). Например: Листинг 8. Уникальность дескриптора
Чтобы сохранять различное поведение для разных экземпляров класса, приходится использовать аргумент Листинг 9. Неуникальный дескриптор
СвойстваСвойства похожи на дескрипторы, но обычно они объявляются внутри определенного класса, а не как "внешние дескрипторы", которыми могут пользоваться сразу несколько классов. Так же, как и у "обычных" дескрипторов, основная идея состоит в том, чтобы объявить функции получения, установки и удаления значения. После этого с помощью специальной функции Как ни странно, идея свойств снова возвращает нас к данному мной в начале статьи короткому описанию работы языка Ruby. Свойство выглядит при использовании как обычный атрибут, но определяется через функции установки значения, его получения и так далее. При желании можно было бы реализовать в Python правила Ruby и вообще не предоставлять доступа к "настоящим" атрибутам класса. Однако скорее всего вы захотите использовать оба подхода. Вот как работают свойства: Листинг 10. Как работают свойства
Имена функций получения/изменения/удаления значения могут быть любыми. Обычно разумно использовать осмысленные имена вроде перечисленных выше. Действительный код этих функций может быть любым, но имеет смысл использовать имена атрибутов с двойным подчеркиванием спереди. Эти атрибуты привязываются к экземпляру по обычным правилам "полу-скрытия" имен Python. При этом сами методы тоже можно использовать: Листинг 11. Использование методов
Правь, анархияВ этой статье я показал множество способов, существующих в языке Python для того, чтобы создать атрибуты, работающие как методы (или являющиеся ими), но у меня нет четкого ответа на вопрос, как справиться со всем этим множеством возможностей. Я бы с удовольствием предложил вам использовать один из этих методов и забыть обо всех остальных как о более неудобных или менее универсальных. К сожалению, у каждого из описанных подходов есть свои преимущества и недостатки. Несмотря на коренные различия в синтаксисе использования, каждый из них применим к определенным реальным задачам программирования. К тому же у меня были смутные мысли о других, гораздо более запутанных и мрачных, способах использования метаклассов, фабрик классов и декораторов, которые могут быть использованы программистом для получения похожих результатов (хотя я и не упомянул их в этой статье). Эти идеи завели бы меня в самые темные места метапрограммирования в Python. Было бы здорово, если бы описанные мной способы были доступны для использования, но их модификации были бы просто параметризованы, а не использовали бы абсолютно различные принципы и синтаксис. Одна из главных целей Python 3000 - упростить всю эту структуру; но до сегодняшнего дня я не увидел никаких конкретных предложений по упорядочению возможностей реализации принципа "атрибуты как методы". Можно было бы, например, сделать в Python декораторы для классов (как сейчас для функций и методов) и ввести стандартный модуль декораторов для наиболее часто используемых моделей таких вот "волшебных атрибутов". Конечно, это просто предположение, и я точно не представляю себе, как это могло бы работать, но мне кажется, что такая уловка могла бы скрыть все эти сложности от тех 95% программистов на Python, которые действительно не хотят лезть в дебри внутреннего устройства языка.
|
|