Python装饰器可选参数

use*_*455 5 python decorator

from functools import wraps
def logged(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print func.__name__ + " was called"
        return func(*args, **kwargs)
    return with_logging

@logged
def f(x):
   """does some math"""
   return x + x * x

print f.__name__  # prints 'f'
print f.__doc__   # prints 'does some math'
Run Code Online (Sandbox Code Playgroud)

鉴于此示例代码,我将如何做@logged(variable)

我试过这个

from functools import wraps
def logged(func):
    def outer(var):
        @wraps(func)
        def with_logging(*args, **kwargs):
            print func.__name__ + " was called"
            return func(*args, **kwargs)
        return with_logging
    return outer
Run Code Online (Sandbox Code Playgroud)

我希望这样执行:logged(func)(session_variable)

但是不起作用.任何的想法?我希望能够做@logged和@logged(var)(甚至@logged(var1,var2))谢谢.

Sea*_*ira 11

这里的诀窍是,你必须反省你得到的东西:

def logged(*setting_args, **setting_kwargs):
    no_args = False
    if len(setting_args) == 1 \
        and not setting_kwargs \
        and callable(setting_args[0]):
        # We were called without args
        func = setting_args[0]
        no_args = True

    def outer(func):
        @wraps(func)
        def with_logging(*args, **kwargs):
            print "{} was called".format(func.__name__)
            print "Setting args are: {}".format(setting_args)
            print "Setting keyword args are: {}".format(setting_kwargs)
            return func(*args, **kwargs)
        return with_logging

    if no_args:
        return outer(func)
    else:
        return outer
Run Code Online (Sandbox Code Playgroud)

这将适用于以下任何一项:

# No arguments
@logged
def some_function(x):
    pass

# One or more arguments
@logged(1, 2, 3)
def some_function(x):
    pass

# One or more keyword arguments
@logged(key=1, another_key=2)
def some_function(x):
    pass

# A mix of the two
@logged(1, 2, key=3)
def some_function(x):
    pass
Run Code Online (Sandbox Code Playgroud)

不会,如果它被称为只有一个可调用的参数工作:

# This will break.
@logged(lambda: "Just for fun")
def some_function(x):
    pass
Run Code Online (Sandbox Code Playgroud)

没有办法区分单个可调用设置和装饰器的无参数调用之间的区别.但是,您可以传递一个垃圾关键字arg来解决这个问题,如果您需要:

# This gets around the above limitation
@logged(lambda: "Just for fun", ignored=True)
def some_function(x):
    pass
Run Code Online (Sandbox Code Playgroud)


Nab*_*med 6

这个问题已经有6年多了,并且已经有了答案。我遇到了同样的情况 - 必须更新代码中很多地方使用的装饰器,并且想要添加一个可选参数。

我能够使用不同的方法来完成它 - 来自《Python CookBook 3rd Edition》一书,第 9 章 - 9.6。定义带有可选 参数的装饰器。它提出问题,提出解决方案并以讨论结束(非常棒)。


解决方案:适用于Python 3.3+

from functools import wraps, partial

def logged(func=None, *, var1=None, var2=None):
    if func is None:
        return partial(logged, var1=var1, var2=var2)

    @wraps(func)
    def with_logging(*args, **kwargs):
        print func.__name__ + " was called"
        return func(*args, **kwargs)
    return with_logging
Run Code Online (Sandbox Code Playgroud)

通过上述操作,您可以执行以下任一操作:

@logged
def f(x):

@logger(var1)
def f(x):

@logger(var1, var2)
def f(x) 
Run Code Online (Sandbox Code Playgroud)

解释(最好在书上找)

要理解代码的工作原理,您需要深入了解装饰器如何应用于函数及其调用约定。

1. 简单的装饰器,例如:

# Example use
@logged
def add(x, y):
    return x + y
Run Code Online (Sandbox Code Playgroud)

调用顺序如下:

def add(x, y):
    return x + y

add = logged(add)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,要包装的函数只是作为第一个参数传递给logging。因此,在解决方案中,logging() 的第一个参数是被包装的函数。所有其他参数都必须有默认值。

2. 装饰器接受参数:

@logged(level=logging.CRITICAL, name='example')
def spam():
    print('Spam!')
Run Code Online (Sandbox Code Playgroud)

调用顺序如下:

def spam():
    print('Spam!')

spam = logged(level=logging.CRITICAL, name='example')(spam)
Run Code Online (Sandbox Code Playgroud)

logged()上面的最后一行是如何调用带有参数的装饰器,即在要装饰的函数的初始调用中spam()未传递,因此我们func=Nonelogged定义中将其设置为可选的装饰器。因此,在第一次调用中仅传递参数。

这反过来又迫使其他参数由关键字指定。此外,当传递参数时,装饰器应该返回一个接受该函数并包装它的函数(参见第 9.5 节)。为此,该解决方案使用了一个巧妙的技巧,涉及functools.partial. 具体来说,它只是返回自身的部分应用版本,其中除了要包装的函数之外的所有参数都是固定的