2008-01-11

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

Вот в этом и состоит причина появления декораторов и их основное назначение. Вообще говоря, нет ничего такого, чего можно было бы добиться с использованием декораторов и нельзя добиться благодаря использованию метаклассов. Однако, использование метаклассов даёт гораздо более запутаный код, чем использование декораторов.

Примеры использования:

  1. Определение функции, которая будет выполняться каждый раз при завершении программы. Обычно в подобных функциях выполняют освобождение памяти, закрытие открытых соединений и другие действия, сопутствующие завершению программы.

    В нашем иллюстративном примере вместо действий мы выведем надпись "Hello, World!":

    def onexit(f): import atexit atexit.register(f) return f

    @onexit def func(): print "Hello, World!"

    Данный пример, вероятно, не очень хорошо подходит для реального использования и служит исключительно для демонстрации. Чтобы проверить его работу, наберите приведённый код в интерпретаторе и выйдите из интерпретатора. В результате вместо обычного выхода вы увидите нечто следующее:

                  >>>
                  Hello, World!
    

  2. Добавление атрибутов к функции. После того, как соответствующий декоратор будет применён к некоторой функции, для этой функции всегда можно будет посмотреть назначенные ей атрибуты. В данном случае, этими атрибутами являются 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'
    
  3. Контролирует количество и типы аргументов функции, а также тип возвращаемого функцией значения. Заметьте, что приведённый код копирует атрибут 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
    
  4. Выполняет функцию, проверяет возникновение в процессе её работы исключения 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/

И вообще статья недописано

Ссылки на эту статью:

План лекций ianytitle.txt
Официальный сайт Zope3 Московская группа изучения реактивного движения The Dream Bot Site noooxml