········# в методе __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 собран с помощью функциональности класса BaseResponse, как и Request. Мы изучим его интерфейс и не будем смотреть на сам код. Взглянем лишь на строку документации для класса BaseResponse, чтобы узнать, как его использовать.
Как объект класса может быть вызываемой функцией? Дело в том, что для него был определен метод BaseRequest.__call__. В следующем примере кода мы покажем лишь этот метод.
Пора извлечь следующий урок: если язык позволяет что-то сделать, почему бы не сделать это? После того как мы поняли, что можно добавить метод __call__() к любому объекту и сделать его вызываемой функцией, мы можем вернуться к оригинальной документации и еще раз перечитать раздел о модели данных в Python (http://docs.python.org/3/reference/datamodel.html).
Подобное разбиение может быть полезно во вспомогательной библиотеке вроде Werkzeug, поскольку она говорит пользователю, какие функции связаны друг с другом, а какие — нет. Разработчик может быть уверен, что атрибуты в одном классе-примеси не будут изменены функциями другого класса-примеси.
В Python для того, чтобы идентифицировать класс-примесь, не нужно ничего, кроме как следовать соглашению, в рамках которого к имени класса добавляется слово «Mixin». Это означает, что, если вы не хотите обращать внимания на порядок разрешения методов, все методы класса-примеси должны иметь разные имена.