简体   繁体   English

将位置参数添加到装饰器

[英]Add positional parameters to decorator

Suppose such a quick decorator example: 假设这样一个快速的装饰器示例:

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

Run it and come by 运行它来

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

I intend to make add_a_book general and refactor it as: 我打算使add_a_book通用并将其重构为:

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

Run it and get: 运行它并获得:

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'

It works as intended, 它按预期工作,
However, it throw errors when I tried symbol @ 但是,当我尝试符号@时会抛出错误

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

How to solve such a problem when add parameters to decorator? 向装饰器添加参数时如何解决这样的问题?

You don't want a decorator, but a decorator factory. 您不需要装饰器,而是装饰器工厂。 In other words, you need a function that acts as a decorator, but returns another decorator. 换句话说,您需要一个充当装饰器但返回另一个装饰器的函数。 This allows you to pass an argument and return a decorator that in turn decorates the function. 这使您可以传递参数并返回装饰器,该装饰器反过来装饰函数。

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'

It's somewhat messy, but you can parametrize decorators like this: 有点混乱,但是您可以像这样对装饰器进行参数化:

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

You can use functools.wraps , and a decorator factory: 您可以使用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())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM