В Python, каковы некоторые примеры того, когда декораторы значительно упрощают задачу?

Попытка найти примеры того, когда декораторы могли бы быть действительно выгодными, и если не так. Пример кода ценится.

5
задан efotinis 19 November 2010 в 21:37
поделиться

5 ответов

Самый простой способ понять полезность декораторов - увидеть несколько примеров. Вот, например, один из них:

Предположим, вы изучаете код и хотите понять, когда и как вызывается та или иная функция. Вы можете использовать декоратор, чтобы изменить функцию чтобы она печатала некоторую отладочную информацию при каждом вызове функции:

import functools
def trace(f):
    '''This decorator shows how the function was called'''
    @functools.wraps(f)
    def wrapper(*arg,**kw):            
        arg_str=','.join(['%r'%a for a in arg]+['%s=%s'%(key,kw[key]) for key in kw])
        print "%s(%s)" % (f.__name__, arg_str)
        return f(*arg, **kw)
    return wrapper

@trace
def foo(*args):
    pass


for n in range(3):
    foo(n)

печатает:

# foo(0)
# foo(1)
# foo(2)

Если бы вы хотели отследить только одну функцию foo, вы, конечно, могли бы добавить более простой код к определению foo:

def foo(*args):
    print('foo({0})'.format(args))

но если у вас много функций, которые вы хотите отследить, или не хотите возиться с оригинальным кодом, тогда декоратор становится полезным.

Другие примеры полезных декораторов см. в библиотека декораторов.

4
ответ дан 18 December 2019 в 09:06
поделиться

Декораторы предназначены для вариантов дизайна, когда вы объединяете две концепции, такие как «Ведение журнала» и «Управление запасами» или «зарегистрированный пользователь» и «Просмотр последних сообщений. ".

  • Одна из этих концепций оборачивает другую, управляя тем, как она вызывается. Эта концепция - декоратор.
  • Вторая концепция навсегда связана с первой, так что вполне нормально потерять возможность напрямую вызывать вторую концепцию. Например, потеря возможности вызывать «Просмотр последних сообщений» без вызова «Зарегистрированный пользователь»

. Когда выбор дизайна правильный, синтаксис декоратора (или синтаксический сахар для декораторов) читается чисто и перемещается чтобы исключить ошибки из-за недопонимания.

Обычные концепции декораторов включают:

  • Ведение журнала. Он может быть объединен с процессором транзакций для регистрации каждой успешной или неудачной транзакции.
  • Требуется безопасность. Это может быть связано с изменением цены в инвентаре.
  • Кэширование (или запоминание).Это может быть связано с вычислениями чистой приведенной стоимости или любой дорогостоящей статической операцией только для чтения.
  • Исправления языка, такие как «@classmethod» или «преобразование возвращаемых значений ошибок в исключения»
  • Регистрация в фреймворках, например, «эта функция вызывается при нажатии этой кнопки .
  • состояние машинная обработка,где декоратор решает, какое состояние обрабатывать дальше.
  • и др. и т. д.

Вы можете посмотреть http://wiki.python.org/moin/PythonDecoratorLibrary (устаревшая вики-страница) или dectools ( http://pypi.python.org / pypi / dectools ) для получения дополнительной документации и примеров.

1
ответ дан 18 December 2019 в 09:06
поделиться

Декораторы - это простой синтаксис для определенного способа вызова функций высшего порядка, поэтому, если вы сосредотачиваетесь только на синтаксисе, вряд ли получится большая разница. IOW, где бы вы ни сказали

@mydecorator
def f(...):
  # body of f

, вы могли бы идентично сказать

def f(...):
  # body of f
f = mydecorator(f)

Преимущество синтаксиса декоратора в том, что он немного более лаконичен (не повторять f три раза ;-) и что он идет перед оператор def (или class , для декораторов классов), таким образом немедленно предупреждая читателя о коде. Это важно, но не может быть отличным !

Семантика декораторов (и, идентично, вызовов функций высшего порядка, соответствующих этому шаблону, если бы декораторов не было ;-). Например,

@classmethod
def f(cls, ...):

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

@property
def foo(self, ...):

позволяет создавать свойства только для чтения (с другими связанными декораторами в 2.6 для свойств, не предназначенных только для чтения ;-), которые чрезвычайно полезны, даже когда не используется (поскольку они избавляют вас от написания множества глупых «шаблонных» аксессоров для того, что по сути является атрибутами ... просто потому, что доступ к атрибуту может потребовать запуска некоторых вычислений в будущем! -).

Помимо встроенных в Python, ваши собственные декораторы могут быть не менее важны - конечно, в зависимости от вашего приложения. В общем, они упрощают рефакторинг некоторой части кода (который в противном случае пришлось бы дублировать во многих функциях и классах [[или вам, возможно, придется прибегнуть к метаклассам для случая класса, но они богаче и сложнее для используйте правильно]]) в декоратор. Таким образом, они помогают избежать повторяющегося, шаблонного кода - и поскольку DRY , «Не повторяйся», является основным принципом разработки программного обеспечения, любая помощь, которую вы можете получить в этом направлении, должна быть сердечно приветствоваться.

8
ответ дан 18 December 2019 в 09:06
поделиться

В AppEngine API есть красивый декоратор @login_required , который может немного очистить код:

class MyPage(webapp.RequestHandler):
    @login_required
    def get(self):
        self.response.headers['Content-Type'] = 'text/plain'
        self.response.out.write("Hello, world!")

В отличие от:

class MyPage(webapp.RequestHandler):
    def get(self):
        user = users.get_current_user()
        if not user:
            return self.redirect(users.create_login_url(self.request.uri))

        self.response.headers['Content-Type'] = 'text/plain'
        self.response.out.write("Hello, world!")

Я чаще всего использую @classmethod для методов класса, @staticmethod для статических методов и (как сказал Майк Десимоун) @property для свойств, доступных только для чтения. Просто удобнее иметь декоратор перед функцией, а не после нее, как в

class Bar(object):
    @classmethod
    def foo(cls):
        return id(cls)

вместо:

class Bar(object):
    def foo(cls):
        return id(cls)
    foo = classmethod(foo)

Он просто сохраняет шаблонный код.

1
ответ дан 18 December 2019 в 09:06
поделиться

Обычный пример - использование декоратора @property для создания свойства только для чтения:

@property
def count(self):
    return self._events

вместо:

def _get_count(self):
    return self._events

count = property(_get_count)
4
ответ дан 18 December 2019 в 09:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: