TLDR
我可以包装多少个实际的装饰器?实际的修饰器是指接受目标函数as作为参数的函数。
在Python中将参数传递给装饰器时,我们会执行以下操作:
def decorator_maker_with_arguments(decorator_arg1, decorator_arg2):
print("I make decorators! And I accept arguments: {0}, {1}".format(decorator_arg1, decorator_arg2))
def my_decorator(func):
# The ability to pass arguments here is a gift from closures.
# If you are not comfortable with closures, you can assume it’s ok,
# or read: https://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python
print("I am the decorator. Somehow you passed me arguments: {0}, {1}".format(decorator_arg1, decorator_arg2))
# Don't confuse decorator arguments and function arguments!
def wrapped(function_arg1, function_arg2) :
print("I am the wrapper around the decorated function.\n"
"I can access all the variables\n"
"\t- from the decorator: {0} {1}\n"
"\t- from the function call: {2} {3}\n"
"Then I can pass them to the decorated function"
.format(decorator_arg1, decorator_arg2,
function_arg1, function_arg2))
return func(function_arg1, function_arg2)
return wrapped
return my_decorator
此代码取自this answer。
请注意,实际装饰器周围有一个包装器,用于处理提供给实际装饰器的参数。
奇怪的是,您可以使用包装器装饰目标函数,而不是像这样使用装饰器:
@decorator_maker_with_arguments("Leonard", "Sheldon")
def decorated_function_with_arguments(function_arg1, function_arg2):
print("I am the decorated function and only knows about my arguments: {0}"
" {1}".format(function_arg1, function_arg2))
decorated_function_with_arguments("Rajesh", "Howard")
#outputs:
#I make decorators! And I accept arguments: Leonard Sheldon
#I am the decorator. Somehow you passed me arguments: Leonard Sheldon
#I am the wrapper around the decorated function.
#I can access all the variables
# - from the decorator: Leonard Sheldon
# - from the function call: Rajesh Howard
#Then I can pass them to the decorated function
#I am the decorated function and only knows about my arguments: Rajesh Howard
所以我的问题是,我可以在实际的装饰器中包装多少个功能?
以下面的代码为例:
def level0(foo):
print("Level 0")
def level1(foo):
print("Level 1")
def level2(foo):
print("Level 2")
def dec(some_func):
print("Level 3")
def wrap():
print("Foo is " + foo)
some_func()
print("Level 3 End")
return wrap
return dec
return level2
return level1
@level0("foo")
def test():
print("From python")
呼叫test
打印
Level 0
Level 1
TypeError: level2() missing 1 required positional argument: 'foo'
那么深度限制只有2个吗?还是我做错了什么?
让我知道我身边是否需要其他任何信息。
答案 0 :(得分:0)
def level0(foo):
print(type(foo))
print("Level 0")
def level1(foo):
print(type(foo))
print("Level 1")
def level2(foo):
print("Level 2")
def dec(some_func):
print("Level 3")
def wrap():
print("Foo is " + foo)
some_func()
print("Level 3 End")
return wrap
return dec
return level2
return level1
尝试一下,您将看到level0和level1中的“ foo”之间的区别。 装饰器只是语法糖。在您的情况下,python会做到
test = level0("foo")(test)
但是如果您的代码更改为此
@level0
def foo():
print("from foo")
python会做
test = level0(test)
答案 1 :(得分:0)
装饰器“覆盖”功能。它调用从以该函数作为参数的函数调用中获得的函数指针。然后使用传递给原始函数的参数调用该函数指针。
您在做什么,正在创建一个返回装饰器的函数,然后调用该函数,如下所示:
def this_returns_decorator(arg_passed_to_this_returns_decorator):
def decorator(function):
def wrapper(arg_passed_to_test):
function(arg_passed_to_this_returns_decorator + "|" + arg_passed_to_test)
return wrapper
return decorator
# next line is "equal" to @decorator
# ie. the function this_returns_decorator is called here and returns a decorator
@this_returns_decorator("arg passed to this_returns_decorator")
def test(arg):
print(arg)
# decorating is equal to "overwriting the function":
# test = this_returns_decorator("arg passed to this_returns_decorator")(test)
# will print "arg passed to this_returns_decorator|arg_passed_to_test"
test("arg_passed_to_test")
这样您就不能获得两个以上的级别。您可以创建一个函数,该函数将返回一个将返回装饰器的函数,依此类推,但我将其解释为函数嵌套,而不是装饰器嵌套。
关于问题:
我可以嵌套装饰器的最高级别是什么?
我想这是您嵌套装饰器的方式:
def w1(f):
def wrapper(name):
return "w1 " + f(name)
return wrapper
def w2(f):
def wrapper(name):
return "w2 " + f(name)
return wrapper
@w2
@w1
def test(name):
return name
print(test("name")) # will print "w2 w1 name"
python语言参考只说装饰器可以嵌套,我找不到任何限制。为了有趣和测试,我创建了以下shell脚本:
#!/bin/bash
set -euo pipefail
tmp=$(mktemp)
trap 'rm $tmp' EXIT
for ((i = 10000;; i*=2)); do
{
for i in $(seq $i); do
printf "%s" "
def w$i(f):
def w():
f()
return w
"
done
echo
for i in $(seq $i); do
printf "%s\n" "@w$i"
done
printf "%s\n" "def test():
print(\"test\")
print(\"Success\")
"
} >"$tmp"
printf "Testing levels i = %d\n" $i
( set -x; python3 "$tmp"; )
done
当我的电脑开始滞后时,能够升高到40万以上。装饰器的级别可能取决于系统拥有的内存量以及python解释器的性能。
@edit:
刚刚发现,不允许在decorator语句内调用函数返回的函数指针。但是,您仍然可以获得指向装饰器的指针,然后重载该函数。
def level0(foo):
print(type(foo))
print("Level 0")
def level1(foo):
print(type(foo))
print("Level 1")
def level2(foo):
print("Level 2")
def dec(some_func):
print("Level 3")
def wrap():
print("Foo is " + foo)
some_func()
print("Level 3 End")
return wrap
return dec
return level2
return level1
# @level0("1")("2")("3") does not work - SyntaxError
decorator = level0("1")("2")("3")
@decorator
def test():
print(test)
test()
修饰符正则表达式(来自python reference)为:
decorator ::= "@" dotted_name ["(" [argument_list [","]] ")"] NEWLINE
(
)
是可选的,只能指定一次。也许我们可以做一个简单的解决方法自己动手:
def workaround(obj, arg):
if len(arg) == 0:
return obj
return workaround(obj(arg[0]), arg[1:])
@workaround(level0, ["1", "2", "3"])
def test2():
print("test2")
那仍然是函数嵌套。