Подтвердить что ты не робот

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

В Python, в чем разница между использованием одного и того же декоратора с круглыми скобками и без них?

Например:

Без скобок:

@some_decorator
def some_method():
    pass

С круглыми скобками:

@some_decorator()
def some_method():
    pass
4b9b3361

Ответ 1

some_decorator в первом фрагменте кода является обычным декоратором:

@some_decorator
def some_method():
    pass

эквивалентно

some_method = some_decorator(some_method)

С другой стороны, some_decorator во втором фрагменте кода является вызываемым, который возвращает декоратор:

@some_decorator()
def some_method():
    pass

эквивалентно

some_method = some_decorator()(some_method)

Как отметил Дункан в комментариях, некоторые декораторы предназначены для работы в обоих направлениях. Вот довольно базовая реализация такого декоратора:

def some_decorator(arg=None):
    def decorator(func):
        def wrapper(*a, **ka):
            return func(*a, **ka)
        return wrapper

    if callable(arg):
        return decorator(arg) # return 'wrapper'
    else:
        return decorator # ... or 'decorator'

pytest.fixture является более сложным примером.

Ответ 2

Некоторый реально работающий код, где вы используете arg внутри декоратора:

def someDecorator(arg=None):
    def decorator(func):
        def wrapper(*a, **ka):
            if not callable(arg):
                print (arg)
                return func(*a, **ka)
            else:
                return 'xxxxx'
        return wrapper

    if callable(arg):
        return decorator(arg) # return 'wrapper'
    else:
        return decorator # ... or 'decorator'

@someDecorator(arg=1)
def my_func():
    print('aaa')

@someDecorator
def my_func1():
    print('bbb')

if __name__ == "__main__":
    my_func()
    my_func1()

Выход:

1
aaa

Ответ 3

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

Ключом к пониманию разницы между @some_decorator и @some_decorator() является то, что первый является декоратором, а второй - функцией (или вызываемой), которая возвращает декоратор.

Я считаю, что наблюдение за реализацией каждого случая помогает понять разницу:

@some_decorator

def some_decorator(func):
    def wrapper(func):
        return func(*args, **kwargs)
    return wrapper

Заявка:

@some_decorator
def some_method():
    pass

Эквивалент:

some_method = some_decorator(some_method)

@some_decorator()

def some_decorator():
    def decorator(func):
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)
        return wrapper
    return decorator

Заявка:

@some_decorator()
def some_method():
    pass

Эквивалент:

some_method = some_decorator()(some_method)

Обратите внимание, что теперь легче видеть, что @some_decorator() - это функция, возвращающая декоратор, а some_decorator - просто декоратор. Имейте в виду, что некоторые декораторы написаны так, чтобы работать в обоих направлениях.

Так что теперь вам может быть интересно, почему у нас есть эти два случая, когда предыдущая версия кажется проще. Ответ в том, что если вы хотите передать аргументы декоратору, использование @some_decorator() позволит вам сделать это. Давайте посмотрим код в действии:

def some_decorator(arg1, arg2):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(arg1)
            print(arg2)
            return func(*args, **kwargs)
        return wrapper
    return decorator

Заявка:

@some_decorator('hello', 'bye')
def some_method():
    pass

Эквивалент:

some_method = some_decorator('hello', 'bye')(some_method)

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