Читаем Автостопом по Python полностью

Декораторы, основанные на классах (питонский способ использовать динамическую типизацию)

Werkzeug применяет утиную типизацию для того, чтобы создать декоратор @cached_property. Когда мы говорили о свойстве, описывая проект Tablib, то упоминали, что оно похоже на функцию. Обычно декораторы являются функциями, но поскольку тип ничем не навязывается, они могут быть любым вызываемым объектом: свойство на самом деле является классом. (Вы можете сказать, что оно задумывалось как функция, поскольку его имя не начинается с прописной буквы, а в PEP 8 говорится, что имена классов должны начинаться c прописной буквы.) При использовании нотации, похожей на вызов функции (property()), будет вызван метод property.__init__() для инициализации и возврата экземпляра свойства — класс, для которого соответствующим образом определен метод __init__(), работает как вызываемая функция. Кря.

В следующем фрагменте кода содержится полное определение свойства cached_property, которое является подклассом класса property. Документация класса cached_property говорит сама за себя. Когда это свойство будет использоваться для декорирования BaseRequest.form в коде, который мы только что видели, instance.form будет иметь тип cached_property и с точки зрения пользователя будет вести себя как словарь, поскольку для него определены методы _get_() и __set__(). При получении доступа к BaseRequest.form в первый раз он считает данные формы (если она существует), а затем запишет их в instance.form.__dict__, чтобы к ним можно было получить доступ в дальнейшем: class cached_property(property):

"""Декоратор, который преобразует функцию в ленивое свойство.

Обернутая функция в первый раз вызывается для получения результата,

затем полученный результат используется при следующем обращении к value::

class Foo(object):

@cached_property

def foo(self):

# выполняем какие-нибудь важные расчеты

return 42

Класс должен иметь '__dict__' для того, чтобы это свойство работало.

"""

# деталь реализации: для подкласса, встроенного

# в Python свойства-декоратора

# мы переопределяем метод _get_ так, чтобы получать кэшированное

# значение.

# Если пользователь хочет вызвать метод _get_ вручную, свойство будет

# работать как обычно, поскольку логика поиска реплицируется

# в методе _get_ при вызове вручную.

def __init__(self, func, name=None, doc=None):

self.__name__ = name or func.__name__

self.__module__ = func.__module__

self.__doc__ = doc or func.__doc__

self.func = func

def __set__(self, obj, value):

obj.__dict__[self.__name__] = value

def _get_(self, obj, type=None):

if obj is None:

return self

value = obj.__dict__.get(self.__name__, _missing)

if value is _missing:

value = self.func(obj)

obj.__dict__[self.__name__] = value

return value

Взглянем на этот код в действии:

>>> from werkzeug.utils import cached_property

>>>

>>> class Foo(object):

... @cached_property

... def foo(self):

... print("You have just called Foo.foo()!")

... return 42

...

>>> bar = Foo()

>>>

>>> bar.foo

You have just called Foo.foo()!

42

>>> bar.foo

42

>>> bar.foo # Обратите внимание, сообщение не выводится снова...

42

Response.__call__

Класс Response собран с помощью функциональности класса BaseResponse, как и Request. Мы изучим его интерфейс и не будем смотреть на сам код. Взглянем лишь на строку документации для класса BaseResponse, чтобы узнать, как его использовать.

В примере, показанном в строках документации, функция index() вызывается в ответ на запрос HTTP. Ответом будет строка Index page.

Эта сигнатура нужна в приложениях WSGI, как указано в PEP 333/PEP 3333.

Класс Response является подклассом BaseResponse, поэтому ответ представляет собой объект класса BaseResponse.

Для ответа 404 требуется лишь установить значение ключевого слова status.

И вуаля — объект response является вызываемой функцией сам по себе, все сопутствующие заголовки и детали имеют разумные значения по умолчанию (либо переопределены, если путь отличается от /).

Как объект класса может быть вызываемой функцией? Дело в том, что для него был определен метод BaseRequest.__call__. В следующем примере кода мы покажем лишь этот метод.

Эта сигнатура позволяет сделать объекты класса BaseResponse вызываемыми функциями.

Здесь учтены требования к вызову приложений WSGI для функции start_response.

А здесь возвращается итерабельный объект типа bytes.

Пора извлечь следующий урок: если язык позволяет что-то сделать, почему бы не сделать это? После того как мы поняли, что можно добавить метод __call__() к любому объекту и сделать его вызываемой функцией, мы можем вернуться к оригинальной документации и еще раз перечитать раздел о модели данных в Python (http://docs.python.org/3/reference/datamodel.html).

Примеси (еще одна отличная штука)

Перейти на страницу:

Похожие книги

100 знаменитых харьковчан
100 знаменитых харьковчан

Дмитрий Багалей и Александр Ахиезер, Николай Барабашов и Василий Каразин, Клавдия Шульженко и Ирина Бугримова, Людмила Гурченко и Любовь Малая, Владимир Крайнев и Антон Макаренко… Что объединяет этих людей — столь разных по роду деятельности, живущих в разные годы и в разных городах? Один факт — они так или иначе связаны с Харьковом.Выстраивать героев этой книги по принципу «кто знаменитее» — просто абсурдно. Главное — они любили и любят свой город и прославили его своими делами. Надеемся, что эти сто биографий помогут читателю почувствовать ритм жизни этого города, узнать больше о его истории, просто понять его. Тем более что в книгу вошли и очерки о харьковчанах, имена которых сейчас на слуху у всех горожан, — об Арсене Авакове, Владимире Шумилкине, Александре Фельдмане. Эти люди создают сегодняшнюю историю Харькова.Как знать, возможно, прочитав эту книгу, кто-то испытает чувство гордости за своих знаменитых земляков и посмотрит на Харьков другими глазами.

Владислав Леонидович Карнацевич

Неотсортированное / Энциклопедии / Словари и Энциклопедии