Попытка найти примеры того, когда декораторы могли бы быть действительно выгодными, и если не так. Пример кода ценится.
Самый простой способ понять полезность декораторов - увидеть несколько примеров. Вот, например, один из них:
Предположим, вы изучаете код и хотите понять, когда и как вызывается та или иная функция. Вы можете использовать декоратор, чтобы изменить функцию чтобы она печатала некоторую отладочную информацию при каждом вызове функции:
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))
но если у вас много функций, которые вы хотите отследить, или не хотите возиться с оригинальным кодом, тогда декоратор становится полезным.
Другие примеры полезных декораторов см. в библиотека декораторов.
Декораторы предназначены для вариантов дизайна, когда вы объединяете две концепции, такие как «Ведение журнала» и «Управление запасами» или «зарегистрированный пользователь» и «Просмотр последних сообщений. ".
. Когда выбор дизайна правильный, синтаксис декоратора (или синтаксический сахар для декораторов) читается чисто и перемещается чтобы исключить ошибки из-за недопонимания.
Обычные концепции декораторов включают:
Вы можете посмотреть http://wiki.python.org/moin/PythonDecoratorLibrary (устаревшая вики-страница) или dectools ( http://pypi.python.org / pypi / dectools ) для получения дополнительной документации и примеров.
Декораторы - это простой синтаксис для определенного способа вызова функций высшего порядка, поэтому, если вы сосредотачиваетесь только на синтаксисе, вряд ли получится большая разница. 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 , «Не повторяйся», является основным принципом разработки программного обеспечения, любая помощь, которую вы можете получить в этом направлении, должна быть сердечно приветствоваться.
В 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)
Он просто сохраняет шаблонный код.
Обычный пример - использование декоратора @property
для создания свойства только для чтения:
@property
def count(self):
return self._events
вместо:
def _get_count(self):
return self._events
count = property(_get_count)