HOWTO use decorators in Zope.txt
2008-01-11 19:03Использование декораторов в Python Введение: Одним из способов преобразования функций и методов (например, объявления их как классов или статических методов) заключается в использовании так называемых декораторов. Данный декоратор встроен в Python и импортирования не требует. Декоратор является встроенным в Python и не требует специального импортирования. import CachedProperty Lazy -- декоратор, обеспечивающий так называемые ленивые вычисления. import Lazy readproperty -- данный декоратор подобен декоратору Lazy за исключением того, что при его использовании не происходит сохранения значения функции в качестве свойства, а просто при повторном вызове вызывается уже имевший место результат выполнения функции. Указаное сообщение выводится в консоль при запуске zope в виде deprecation warning. import deprecate invariant -- функции или методы, отмеченные данным декоратором, будут вызваны с объектом (предоставляющим интерфейс класса, вну ...
Использование декораторов в Python
Введение:
Одним из способов преобразования функций и методов (например, объявления их как классов или статических методов) заключается в использовании так называемых декораторов. Данный метод довольно неуклюж и не является обязательным для использования. Декораторы, как правило, пишутся перед кодом, который без их использования является более сложным для понимания. В идеале, "декоративные" преобразования функций и методов должны производиться на том же самом участке кода, где происходит объявление функции или метода, над которыми необходимо провести это преобразование. Рассмотрим использование декораторов более подробно.
Причины появления и назначение:
Описываемый в статье метод является трансформацией другого употребляемого в настоящее время способа: произведения желаемого преобразования над функцией непосредственно после её написания. В большинстве случаев это позволяет разделить методы поведения объекта и функции получения и установки свойств объека. Примером такого преобразования может служить следующий код:
def foo(self): # some method body statements foo = classmethod(foo)
Чем больше кода содержат функции, тем менее читаемым становится код, подобный приведённому. Кроме того, троекратное декларирование имени одной и той же функции - не очень грамотный подход с точки зрения программирования. Решение данной проблемы заключается в перенесении преобразования функции или метода ближе к объявлению этой функции или метода (ближе к заголовку функции). Таким образом, решение заключается в том, чтобы заменить указанный код:
def foo(cls): pass foo = synchronized(lock)(foo) foo = classmethod(foo)
на альтернативный, размещающий модификторы непосредственно над заголовком метода или функции:
@classmethod @synchronized(lock) def foo(cls): pass
Вот в этом и состоит причина появления декораторов и их основное назначение. Вообще говоря, нет ничего такого, чего можно было бы добиться с использованием декораторов и нельзя добиться благодаря использованию метаклассов. Однако, использование метаклассов даёт гораздо более запутаный код, чем использование декораторов.
Примеры использования:
- Определение функции, которая будет выполняться каждый раз при
завершении программы. Обычно в подобных функциях выполняют
освобождение памяти, закрытие открытых соединений и другие
действия, сопутствующие завершению программы.
В нашем иллюстративном примере вместо действий мы выведем надпись "Hello, World!":
def onexit(f): import atexit atexit.register(f) return f
@onexit def func(): print "Hello, World!"
Данный пример, вероятно, не очень хорошо подходит для реального использования и служит исключительно для демонстрации. Чтобы проверить его работу, наберите приведённый код в интерпретаторе и выйдите из интерпретатора. В результате вместо обычного выхода вы увидите нечто следующее:
>>> Hello, World!
- Добавление атрибутов к функции. После того, как соответствующий
декоратор будет применён к некоторой функции, для этой функции
всегда можно будет посмотреть назначенные ей атрибуты. В данном
случае, этими атрибутами являются versionadded и author. Что
делает функция, к которой применён декоратор - не имеет значения,
а пример использования декораторов подобным образом можно увидеть
в следующем коде:
>>> def attrs(**kwds): ... def decorate(f): ... for k in kwds: ... setattr(f, k, kwds[k]) ... return f ... return decorate ... >>> >>> @attrs(versionadded="2.2", ... author="Guido van Rossum") ... def mymethod(f): ... print "this is mymethod" ... >>> >>> mymethod <function mymethod at 0xb7c4b534> >>> dir(mymethod) ['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__get__', '__getattribute__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 'author', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name', 'versionadded'] >>> mymethod.author 'Guido van Rossum' >>> mymethod.versionadded '2.2'
- Контролирует количество и типы аргументов функции, а также тип
возвращаемого функцией значения. Заметьте, что приведённый код
копирует атрибут func_name (имя старой функции) в имя новой.
func_name (имя функции) было сделано доступным для записи начиная
с Python 2.4a3:
def accepts(*types): def check_accepts(f): assert len(types) == f.func_code.co_argcount def new_f(*args, **kwds): for (a, t) in zip(args, types): assert isinstance(a, t), \ "arg %r does not match %s" % (a,t) return f(*args, **kwds) new_f.func_name = f.func_name return new_f return check_accepts def returns(rtype): def check_returns(f): def new_f(*args, **kwds): result = f(*args, **kwds) assert isinstance(result, rtype), \ "return value %r does not match %s" % (result,rtype) return result new_f.func_name = f.func_name return new_f return check_returns @accepts(int, (int,float)) @returns((int,float)) def func(arg1, arg2): return arg1 * arg2
- Выполняет функцию, проверяет возникновение в процессе её работы
исключения TypeError, и если оно возникает, в качестве результата
работы функции возвращается u"". Если TypeError не возникает,
возвращается обычный результат работы функции. Обратите внимание
на простоту и элегантность кода данного декоратора в сочетании с
его очевидной полезностью. Кроме того, на примере данного
декоратора прекрасно видна общая схема написания любых
декораторов:
def catch(f) : def c(self,*kv,**kw) : try : return f(self,*kv,**kw) except TypeError,msg : return u"" return c
Справочник по декораторам:
- property
- декоратор, говорящий о том, что результат работы метода будет использоваться как значение свойства с именем, соответствующим имени вызываемого метода. Данный декоратор встроен в Python и импортирования не требует.
- staticmethod
- декоратор, сообщающий о том, что функция является статическим методом. Статические методы не принимают неявный первый аргумент (self) и могут быть вызваны как для уже созданного объекта (С().mymethod()), так и без создания объекта путём обращения к классу (С.mymethod()). Декоратор является встроенным в Python и не требует специального импортирования.
- CachedProperty
- декоратор, обеспечивающий кеширование результатов
вычислений и их сохранение и выдачу без повторного вызова
функции до тех пор, пока не произойдёт изменение каких-либо
свойств, влияющих на результат работы этой функции.
Местоположение исходников:
zope/cachedscriptors/property.py
Пример импорта:
from zope.cachedescriptors.property import CachedProperty
- Lazy
- декоратор, обеспечивающий так называемые ленивые вычисления.
Отдает результат вычисления функции, независимо от числа
обращения к ней, вызывая эту функцию на исполнение только
один раз при первом обращении к ней.
Если есть необходимость произвести вызов функции и её выполнение повторно, необходимо удалить результаты её вызова с помощью операции:
del имя_ленивой_функции
Местоположение исходников:
zope/cachedscriptors/property.py
Пример импорта:
from zope.cachedescriptors.property import Lazy
- readproperty
- данный декоратор подобен декоратору Lazy за
исключением того, что при его использовании не происходит
сохранения значения функции в качестве свойства, а просто при
повторном вызове вызывается уже имевший место результат
выполнения функции.
Местоположение исходников:
zope/cachedscriptors/property.py
Пример импорта:
from zope.cachedescriptors.property import readproperty
- implementer
- декоратор позволяет объявить интерфейсы, реализуемые
фабриками (кроме классов!!!). Это способ выполнить для
функций-фабрик, те действия, которые для классов достигаются с
помощью implements.
Местоположение исходников:
zope/interface/declaration.py
Пример импорта:
from zope.interface import implementer
- non_overridable
- используется в тех методах проксирующих объектах,
которые не должны быть перегружены методами проксируемымых
объектов.
Местоположение исходников:
zope/proxy/__init__.py
Пример импорта:
from zope.proxy import non_overridable.
- adapter
- используется для указания того, что вызываемый объект
является адаптером некоторого интерфейса. Часто соседствует с
декоратором implementer.
Местоположение исходников:
zope/component/_declaration.py
Пример импорта:
from zope.component import adapter
- deprecate
- декоратор сообщающий о том, что метод или функция в
скором времени прекратят своё существование.
Обычно сопровождается собщением о том, в какой конкретно версии языка или среды публикации произойдёт исчезновение метода. Указаное сообщение выводится в консоль при запуске zope в виде deprecation warning.
Местоположение исходников:
zope/deprecation/deprecation.py
Пример импорта:
from zope.deprecation import deprecate
- invariant
- функции или методы, отмеченные данным декоратором,
будут вызваны с объектом (предоставляющим интерфейс класса,
внутри которого описан инвариант) в качестве параметра.
Декоратор обычно определяется внутри класса, и с объектом именно
этого класса он и будет вызван.
Внутри инварианта происходит проверка значения свойств объекта на различные ограничения. В случае невыполнения требуемых ограничений инвариант должен выдавать соответствующее исключение, как правило Invalid (zope.interface.Invalid).
В качестве примера использования инварианта можно привести код из zope.interface/README.ru.txt, демонстрирующий использование данного декоратора для проверки того, чтобы свойства min и max класса Range были в нормальном соотношении (min не должен быть больше max). Класс RangeError нужен для вывода сообщений об ошибке. range_invariant - инвариант, проверяющий соотношение между свойствами min и max. IRange - интерфейс, для которого будет определён инвариант range_invariant, Range - класс, который будет подвергаться проверке инвариантом. Метод validateInvariants позволяет проверить инварианты для интерфейса, от которого он вызван:
>>> class RangeError(zope.interface.Invalid): ... """A range has invalid limits""" ... def __repr__(self): ... return "RangeError(%r)" % self.args >>> def range_invariant(ob): ... if ob.max < ob.min: ... raise RangeError(ob) >>> class IRange(zope.interface.Interface): ... min = zope.interface.Attribute("Lower bound") ... max = zope.interface.Attribute("Upper bound") ... ... zope.interface.invariant(range_invariant) >>> class Range(object): ... zope.interface.implements(IRange) ... ... def __init__(self, min, max): ... self.min, self.max = min, max ... ... def __repr__(self): ... return "Range(%s, %s)" % (self.min, self.max) >>> IRange.validateInvariants(Range(1,2)) >>> IRange.validateInvariants(Range(1,1)) >>> IRange.validateInvariants(Range(2,1)) Traceback (most recent call last): ... RangeError: Range(2, 1)
Местоположение исходников:
zope/interface/interface.py
Пример импорта:
from zope.interface import invariant
Дополнительная информация
http://www.python.org/dev/peps/pep-0318/
И вообще статья недописано