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'
鉴于此示例代码,我将如何能够@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
我希望像这样执行:logged(func)(session_variable)
但是不起作用。 任何的想法?我想能够做@logged和@logged(var)(甚至@logged(var1,var2)) 感谢。
答案 0 :(得分: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
这适用于以下任何一种情况:
# 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
如果仅使用一个可调用参数调用它将不会工作:
# This will break.
@logged(lambda: "Just for fun")
def some_function(x):
pass
无法区分单个可调用设置和装饰器的无参数调用之间的区别。但是,如果需要,您可以传递一个垃圾关键字arg来解决这个问题:
# This gets around the above limitation
@logged(lambda: "Just for fun", ignored=True)
def some_function(x):
pass
答案 1 :(得分:1)
该问题已有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
在上面,您可以执行以下任一操作:
@logged
def f(x):
@logger(var1)
def f(x):
@logger(var1, var2)
def f(x)
说明(最好在书中查找)
要了解代码的工作原理,您需要拥有 了解装饰器如何应用于功能及其 调用约定。
1。这样的简单装饰器:
# Example use
@logged
def add(x, y):
return x + y
调用顺序如下:
def add(x, y):
return x + y
add = logged(add)
在这种情况下,只需将要包装的函数传递给log作为 第一个论点。因此,在解决方案中, logging()是要包装的函数。所有其他论点 必须具有默认值。
2。装饰者接受参数:
@logged(level=logging.CRITICAL, name='example')
def spam():
print('Spam!')
调用顺序如下:
def spam():
print('Spam!')
spam = logged(level=logging.CRITICAL, name='example')(spam)
最后一行是如何调用带有参数的装饰器,即在logged()
的初始调用中,未传递要装饰的函数spam()
,因此我们在装饰器中将其设置为可选,即{ func=None
定义中的{1}}。因此,在第一次调用中,仅传递了参数。
这反过来又迫使其他参数由关键字指定。 此外,当传递参数时,装饰器应该 返回一个接受该函数并将其包装的函数(请参见食谱 9.5)。为此,该解决方案使用了一个巧妙的技巧,涉及到
logged
。 具体地说,它只是返回部分应用的 本身的版本,其中除了 要包装的功能。
答案 2 :(得分:0)
将def outer(var)
向外,即
def outer(var):
def logged(func):
...
,然后使用@outer(somevar)
作为您的函数,这样可以。
答案 3 :(得分:0)
您尝试的代码中存在轻微错误。而不是使用func
>参数创建嵌套函数。 var
> *args, **kwargs
,订单应为var
> func
> *args, **kwargs
。
以下是满足您要求的代码段。
from functools import wraps
def logged(var=None):
def outer(func):
@wraps(func)
def with_logging(*args, **kwargs):
print func.__name__ + " was called"
return func(*args, **kwargs)
return with_logging
return outer
您可以将此装饰者称为:
@logged
def func1():
...
OR,
@logged(xyz)
def func2():
...
要了解有关装饰器如何工作的更多信息,请参阅文章Decorators with optional arguments。
答案 4 :(得分:0)
Sean Vieira的另一种可能的解决方案可能是:
from functools import wraps
import inspect
def decorator_defaults(**defined_defaults):
def decorator(f):
args_names = inspect.getargspec(f)[0]
def wrapper(*new_args, **new_kwargs):
defaults = dict(defined_defaults, **new_kwargs)
if len(new_args) == 0:
return f(**defaults)
elif len(new_args) == 1 and callable(new_args[0]):
return f(**defaults)(new_args[0])
else:
too_many_args = False
if len(new_args) > len(args_names):
too_many_args = True
else:
for i in range(len(new_args)):
arg = new_args[i]
arg_name = args_names[i]
defaults[arg_name] = arg
if len(defaults) > len(args_names):
too_many_args = True
if not too_many_args:
final_defaults = []
for name in args_names:
final_defaults.append(defaults[name])
return f(*final_defaults)
if too_many_args:
raise TypeError("{0}() takes {1} argument(s) "
"but {2} were given".
format(f.__name__,
len(args_names),
len(defaults)))
return wrapper
return decorator
@decorator_defaults(start_val="-=[", end_val="]=-")
def my_text_decorator(start_val, end_val):
def decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
return "".join([f.__name__, ' ', start_val,
f(*args, **kwargs), end_val])
return wrapper
return decorator
@decorator_defaults(end_val="]=-")
def my_text_decorator2(start_val, end_val):
def decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
return "".join([f.__name__, ' ', start_val,
f(*args, **kwargs), end_val])
return wrapper
return decorator
@my_text_decorator
def func1a(value):
return value
@my_text_decorator()
def func2a(value):
return value
@my_text_decorator2("-=[")
def func2b(value):
return value
@my_text_decorator(end_val=" ...")
def func3a(value):
return value
@my_text_decorator2("-=[", end_val=" ...")
def func3b(value):
return value
@my_text_decorator("|> ", " <|")
def func4a(value):
return value
@my_text_decorator2("|> ", " <|")
def func4b(value):
return value
@my_text_decorator(end_val=" ...", start_val="|> ")
def func5a(value):
return value
@my_text_decorator2("|> ", end_val=" ...")
def func5b(value):
return value
print(func1a('My sample text')) # func1a -=[My sample text]=-
print(func2a('My sample text')) # func2a -=[My sample text]=-
print(func2b('My sample text')) # func2b -=[My sample text]=-
print(func3a('My sample text')) # func3a -=[My sample text ...
print(func3b('My sample text')) # func3b -=[My sample text ...
print(func4a('My sample text')) # func4a |> My sample text <|
print(func4b('My sample text')) # func4b |> My sample text <|
print(func5a('My sample text')) # func5a |> My sample text ...
print(func5b('My sample text')) # func5b |> My sample text ...
注意:它有同样的缺点,你不能将1个参数作为函数传递给装饰器,但如果你想在多个装饰器上使用这个功能,你可以避开代码样板。