Добавить позиционные параметры в декоратор - PullRequest
0 голосов
/ 26 мая 2018

Предположим, такой пример быстрого декоратора:

def read_a_book():
    return "I am reading the book: "
def add_a_book(func):
    def wrapper():
        return func() + "Python Cookbook"
    return wrapper

Запустите его и перейдите к

In [7]: read_a_book = add_a_book(read_a_book)
In [8]: read_a_book()
Out[8]: 'I am reading the book: Python'

Я собираюсь сделать add_a_book общим и изменить его следующим образом:

def add_a_book(func, book):
    def wrapper():
        return func() + book
    return wrapper

Запустите его и получите:

In [7]: read_a_book = add_a_book(read_a_book,"Python")
In [8]: read_a_book()
Out[8]: 'I am reading the book: Python'

Работает как задумано,
Однако выдает ошибки, когда я пробовал символ @

In [10]: @add_a_book("Python")
    ...: def read_a_book():
    ...:     return "I am reading the book: "
TypeError: add_a_book() missing 1 required positional argument: 'book'
#additionaly
In [11]: @add_a_book
   ...: def read_a_book():
   ...:     return "I am reading the book: "
TypeError: add_a_book() missing 1 required positional argument: 'book'

In [12]: @add_a_book()
...: def read_a_book("python"):
...:     return "I am reading the book: "
                       ^
 SyntaxError: invalid syntax

Как решить такиепроблема при добавлении параметров в декоратор?

Ответы [ 3 ]

0 голосов
/ 26 мая 2018

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

def add_a_book(book='Python'):
    def decorator(func):
        def out_fn(*args, **kwargs):
            return str(func(*args, **kwargs)) + str(book)
        return out_fn
    return decorator

@add_a_book('Hello World')
def read_a_book():
    return "I am reading the book: "

read_a_book()
# returns:
'I am reading the book: Hello World'
0 голосов
/ 26 мая 2018

Можно использовать functools.wraps, а декоратор фабрики:

from functools import wraps


def add_a_book(book=''):
    def _add_a_book(f):
        @wraps(f)
        def wrapper(*args):
            r = f() + book
            return r
        return wrapper
    return _add_a_book

@add_a_book(book='my book')
def read_a_book():
    return "I am reading the book: "

if __name__ == '__main__':
    print(read_a_book())
0 голосов
/ 26 мая 2018

Это немного грязно, но вы можете параметризовать декораторы так:

>>> def add_a_book(book):
...     def add_a_book_real(func):
...         def wrapper(*args, **kwargs):
...             return func(*args, **kwargs) + book
...         return wrapper
...     return add_a_book_real
...
>>> @add_a_book("Python")
... def read_a_book():
...     return "I am reading the book: "
...
>>> read_a_book()
'I am reading the book: Python'
>>>
...