将位置参数添加到装饰器

时间:2018-05-26 02:56:27

标签: python decorator

假设有这样一个快速装饰器的例子:

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 :(得分:3)

你不需要装饰师,而是装饰工厂。换句话说,您需要一个充当装饰器的函数,但返回另一个装饰器。这允许您传递一个参数并返回一个装饰器,该装饰器依次修饰该函数。

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'

答案 1 :(得分:1)

它有些混乱,但你可以像这样参数化装饰器:

>>> 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'
>>>

答案 2 :(得分:1)

您可以使用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())