В Python, каковы некоторые примеры, когда декораторы значительно упрощают задачу? - PullRequest
5 голосов
/ 28 марта 2010

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

Ответы [ 5 ]

8 голосов
/ 28 марта 2010

Декораторы - это простой синтаксис для определенного способа вызова функций более высокого порядка, поэтому, если вы сосредоточены только на синтаксисе, вряд ли будет большая разница. 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 , «Не повторяйте себя», является основным принципом разработки программного обеспечения, любая помощь, которую вы можете получить в этом направлении, должна быть от всей души приветствоваться.

4 голосов
/ 28 марта 2010

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

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

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 голосов
/ 28 марта 2010

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

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

вместо:

def _get_count(self):
    return self._events

count = property(_get_count)
1 голос
/ 28 марта 2010

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

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

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

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

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

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

1 голос
/ 28 марта 2010

В API AppEngine есть красивый @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)

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

...