Это довольно субъективно сказать, есть ли «преимущество» для каждого метода.
Однако хорошее понимание того, что происходит под капотом, сделало бы его естественным для того, чтобы выбрать лучший выбор для каждого случая.
Декоратор (говорящий о декораторах функций), является просто вызываемым объектом, который принимает функцию в качестве входного параметра. У Python есть довольно интересный дизайн, который позволяет создавать другие типы вызываемых объектов, кроме функций, и можно использовать это для использования в качестве более удобного или более короткого кода. .
Декораторы были добавлены обратно в Python 2.3 как «синтаксический ярлык» для
def a(x):
...
a = my_decorator(a)
Кроме того, мы обычно называем декоратор некоторыми «вызываемыми объектами», которые предпочли бы быть «декоратор фабрикой» - когда мы используем этот вид :
@my_decorator(param1, param2)
def my_func(...):
...
вызов делается на «my_decorator» с param1 и Param2 - это то возвращает объект, который будет вызываться снова, на этот раз имея «my_func» в качестве параметра. Итак, в этом случае технически «декоратор» - это то, что возвращается «my_decorator», что делает его «фабрикой декораторов» .
Теперь как декораторы, так и «фабрики декораторов», как описано, обычно должны содержать некоторое внутреннее состояние. В первом случае единственное, что он поддерживает, это ссылка на исходную функцию (переменная, называемая f
в ваших примерах). «Завод декораторов» может захотеть зарегистрировать дополнительные переменные состояния («param1» и «param2» в примере выше).
Это дополнительное состояние, в случае декораторов, написанных как функции, хранится в переменных внутри закрывающих функций и доступно как «нелокальные» переменные фактической функцией обертки. Если писать собственный класс, они могут храниться как переменные экземпляра в функции декоратора (который будет рассматриваться как «вызываемый объект», а не «функция»), и доступ к ним более явный и читаемый.
Таким образом, для большинства случаев вопрос о том, предпочитаете ли вы один из подходов или другого: для коротких простых декораторов, функциональный подход часто более читабельен, чем один, написанный как класс, а иногда и более разработать один - особенно один «завод декораторов» будет в полной мере использовать «плоский лучше, чем вложенный» совет для кодирования Python.
Рассмотрим:
def my_dec_factory(param1, param2):
...
...
def real_decorator(func):
...
def wraper_func(*args, **kwargs):
...
#use param1
result = func(*args, **kwargs)
#use param2
return result
return wraper_func
return real_decorator
против этого "гибрида" решения:
class MyDecorator(object):
"""Decorator example mixing class and function definitions."""
def __init__(self, func, param1, param2):
self.func = func
self.param1, self.param2 = param1, param2
def __call__(self, *args, **kwargs):
...
#use self.param1
result = self.func(*args, **kwargs)
#use self.param2
return result
def my_dec_factory(param1, param2):
def decorator(func):
return MyDecorator(func, param1, param2)
return decorator
обновления: Недостающий "чистого класса" форма декораторов
Теперь, обратите внимание на "гибрид" метод берет «лучшее из обоих миров», пытаясь сохранить самый короткий и читаемый код. Полный «декоратор фабрика» определяется исключительно с классами будет необходимо либо два класса, или атрибут «режим», чтобы знать, если он был призван, чтобы зарегистрировать украшенную функцию или на самом деле назвать конечную функцию:
class MyDecorator(object):
"""Decorator example defined entirely as class."""
def __init__(self, p1, p2):
self.p1 = p1
...
self.mode = "decorating"
def __call__(self, *args, **kw):
if self.mode == "decorating":
self.func = args[0]
self.mode = "calling"
return self
# code to run prior to function call
result = self.func(*args, **kw)
# code to run after function call
return result
@MyDecorator(p1, ...)
def myfunc():
...
И, наконец, чистый, «белый воротничок» декоратор определяется с двумя классами - может держать вещи более разделены, но увеличение избыточности до точки, нельзя сказать, что это более ремонтопригодны:
class Stage2Decorator(object):
def __init__(self, func, p1, p2, ...):
self.func = func
self.p1 = p1
...
def __call__(self, *args, **kw):
# code to run prior to function call
...
result = self.func(*args, **kw)
# code to run after function call
...
return result
class Stage1Decorator(object):
"""Decorator example defined as two classes.
No "hacks" on the object model, most bureacratic.
"""
def __init__(self, p1, p2):
self.p1 = p1
...
self.mode = "decorating"
def __call__(self, func):
return Stage2Decorator(func, self.p1, self.p2, ...)
@Stage1Decorator(p1, p2, ...)
def myfunc():
...
Одна важная вещь: фактические функции обертки называют оригинальный 'f' функции, но не возвращает его возвращаемое значение вызываемым: это, скорее всего, приведет к некорректному поведению. – jsbueno
Возможный дубликат [Разница между классами декораторов и функциями декоратора] (http://stackoverflow.com/questions/4650333/difference-between-decorator-classes-and-decorator-functions) –