Разница между классами декоратора и функциями декоратора - PullRequest
11 голосов
/ 10 января 2011

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

Класс декоратора:

class decorator(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print 'something'
        self.func(*args, **kwargs)

Функция декоратора:

def decorator(func):
    def wrapper(*args, **kwargs):
        print 'something'
        return func(*args, **kwargs)
    return wrapper

Использование одного или другого - дело вкуса?Есть ли практическая разница?

Ответы [ 2 ]

13 голосов
/ 10 января 2011

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

class counted(object):
    """ counts how often a function is called """
    def __init__(self, func):
        self.func = func
        self.counter = 0

    def __call__(self, *args, **kwargs):
        self.counter += 1
        return self.func(*args, **kwargs)


@counted
def something():
    pass

something()
print something.counter

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

3 голосов
/ 10 января 2011

Обычно это дело вкуса.Большинство программ на Python используют типизацию утилит, и на самом деле все равно, является ли вызываемая ими функция или экземпляром какого-либо другого типа, если она вызывается.И все, что связано с методом __call__(), может вызываться.

У использования декораторов в стиле функции есть несколько преимуществ:

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

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

  • Большинство инструментов, которые помогают создавать декораторы, такие как functools.wraps() или модуль decorator, сохраняющий подпись Микеле Симионато, работают с декораторами в стиле функций..

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

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

...