![](/img/trans.png)
[英]Understanding the use of multiple python decorators in a single statement
[英]Multiple decorators on single function
如何将多个装饰器添加到 function 以便每个配置组合成一个最终字典?
我的解释可能没有意义,但看看这个例子。
def instruction(name):
def decorator(callback):
def wrapper(self, *args, **kwargs):
# somehow combine every additional dict into this dict
return {
"name": name,
"callback": lambda: callback(self, *args, **kwargs)
}
return wrapper
return decorator
def layout(config):
def decorator(_):
return {"layout": config}
return decorator
# example decorator
def documentation(text):
def decorator(_):
return {"documentation": text}
return decorator
@instruction("poke")
@layout({"address": 2, "data": 1})
@documentation("handle poke instruction") # additional decorators
def instruction_poke(self):
address = self.serial_read_bytes(2)
data = self.serial_read_bytes(1)
print(f"poke {address=} {data=}")
# ...
def main():
self = ... # object with the serial_read_bytes method
instruction = instruction_poke(self)
print(instruction)
# expected output
# {
# "name": poke,
# "documentation": "handle poke instruction",
# "layout": {"address": 2, "data": 1},
# "callback": <lambda to original function>
# }
if __name__ == "__main__":
main()
我遇到了麻烦,因为多个装饰器具有类似堆栈的行为。
我目前的方法是将每个装饰器的字段存储在一个 constants.py 文件中,在我看来,这很麻烦,要弄乱几个文件。
我很感激你的时间:)
您可以创建另一个级别的闭包来概括装饰器配置器函数的创建。
The wrapper function should distinguish between an actual callback function and a stacked decorator, which can be done by testing if the code object of the callback function is that of the wrapper function itself, in which case it should simply return the dict returned by callback function添加了给定的键值对:
def add_config(key):
def configurer(value):
def decorator(callback):
def wrapper(*args, **kwargs):
if callback.__code__ is wrapper.__code__:
return {key: value, **callback(*args, **kwargs)}
return {key: value, 'callback': lambda: callback(*args, **kwargs)}
return wrapper
return decorator
return configurer
instruction, layout, documentation = map(
add_config, ['name', 'layout', 'documentation']
)
@instruction('poke')
@layout({"address": 2, "data": 1})
@documentation("handle poke instruction")
def instruction_poke(i):
return i + 1
instruction = instruction_poke(2)
print(instruction)
print(instruction['callback']())
这输出:
{'name': 'poke', 'layout': {'address': 2, 'data': 1}, 'documentation': 'handle poke instruction', 'callback': <function add_config.<locals>.configurer.<locals>.decorator.<locals>.wrapper.<locals>.<lambda> at 0x7fd9476fad30>}
3
根据您对 go 的要求,有多种选择。 @blhsing 的那个当然效果很好。 这里还有一些:
def wrap_with_info(**kwargs):
def decorator(obj):
if isinstance(obj, dict):
return obj | kwargs
# `obj` must be a function
assert callable(obj)
return kwargs | {"callback": obj}
return decorator
这允许您像这样装饰:
@wrap_with_info(
name="poke",
documentation="handle poke instruction",
layout={"address": 2, "data": 1},
)
def your_function():
pass
也像这样:
@wrap_with_info(name="poke")
@wrap_with_info(documentation="handle poke instruction")
@wrap_with_info(layout={"address": 2, "data": 1})
def your_function():
pass
请注意,此方法本质上总是将your_function
转换为字典:
print(your_function)
给出这个 output:
{'layout': {'address': 2, 'data': 1}, 'callback': <function your_function at 0x7fd1184ae710>, 'documentation': 'handle poke instruction', 'name': 'poke'}
from functools import wraps
def add_info(**kwargs):
def decorator(func):
def wrapper(*func_args, **func_kwargs):
@wraps(func)
def callback():
return func(*func_args, **func_kwargs)
return kwargs | {"callback": callback}
return wrapper
return decorator
然后你也这样装饰:
@add_info(
name="poke",
documentation="handle poke instruction",
layout={"address": 2, "data": 1},
)
def another_function():
pass
但是现在another_function
仍然是一个可调用的,它返回包含原始 function (包装)和附加数据的字典:
print(another_function())
给出这个 output:
{'name': 'poke', 'documentation': 'handle poke instruction', 'layout': {'address': 2, 'data': 1}, 'callback': <function another_function at 0x7fb4ae7a67a0>}
functools.wraps
装饰器确保当我们将原始函数的签名粘贴到callback
中时保留它。 这就是为什么你在 output 中看到它的名字,而不是像...local.callback...
之类的东西。
(旁注:我使用|
表示法来组合字典,自 Python 3.9 起可用。如果这对您不起作用,您可以使用其他方式,例如dict.update
。)
看起来你对装饰器所做的只是将键值对添加到围绕实际 function 的信息字典中。 这种方法不需要使用多个装饰器。 相反,您可以根据 function 传递任意数量的键值对。
它们也可以动态构建,如果这是您需要的,并且如果您提前知道要添加到函数中的数据:
info_x = {"spam": 1, "eggs": 2}
info_y = {"info": "abc"}
info_z = {"something": ["else"]}
...
@add_info(**info_x, **info_y)
def foo():
pass
@add_info(**info_y, **info_z)
def bar():
pass
@wrap_with_info(**info_z, **info_x)
def baz():
pass
print(foo())
print(bar())
print(baz)
给出这个 output:
{'spam': 1, 'eggs': 2, 'info': 'abc', 'callback': <function foo at 0x7f5d1abb29e0>}
{'info': 'abc', 'something': ['else'], 'callback': <function bar at 0x7f5d1abb29e0>}
{'something': ['else'], 'spam': 1, 'eggs': 2, 'callback': <function baz at 0x7efd492aeb00>}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.