带参数和不带参数的上下文装饰器
Context decorator that works with and without arguments
我想将上下文装饰器与使用或不使用参数的可能性结合起来。
让我们从有参数和无参数的装饰器开始,例如:
import functools
def decorator(func=None, *, label=""):
if func is None:
return functools.partial(decorator, label=label)
@functools.wraps(func)
def wrap(*args, **kwargs):
result = func(*args, **kwargs)
print(f"RESULT {label}: {result}")
return result
return wrap
if __name__ == "__main__":
@decorator(label="with arguments")
def dec_args():
return 1
@decorator
def dec_no_args():
return 0
dec_args()
dec_no_args()
还有 ContextDecorator
可以用作上下文管理器或装饰器:
from contextlib import ContextDecorator
class ctxtdec(ContextDecorator):
def __init__(self, label:str=""):
self.label = label
print(f"initialized {self.label}")
def __enter__(self):
print(f"entered {self.label}")
def __exit__(self, exc_type, exc_value, traceback):
print(f"exited {self.label}")
if __name__ == "__main__":
def testfunc():
for n in range(10 ** 7):
n ** 0.5
@ctxtdec("decorated")
def decorated():
testfunc()
with ctxtdec("square rooting"):
testfunc()
decorated()
但我也希望它也能工作:
@ctxtdec
def decorated():
testfunc()
警告:它并不漂亮,我永远不会真正使用它,但我很好奇所以我让它工作了。可能有人也可以清理一下。
诀窍是让上下文装饰器的 metaclass 成为 ContextDecorator
本身,然后覆盖 __call__
方法检查它是否正在传递标签(正常情况)或函数(paren-less 情况)。
from contextlib import ContextDecorator
class CtxMeta(type, ContextDecorator):
def __enter__(self):
print(f"entered <meta-with>")
def __exit__(self, exc_type, exc_value, traceback):
print(f"exited <meta-with>")
def __call__(cls, func_or_label=None, *args, **kwds):
if callable(func_or_label):
return type.__call__(cls, "<meta-deco>", *args, **kwds)(func_or_label)
return type.__call__(cls, func_or_label, *args, **kwds)
然后,您的原始装饰器 class 与以前保持不变,但添加了元 class 声明:
class ctxtdec(ContextDecorator, metaclass=CtxMeta):
def __init__(self, label:str=""):
self.label = label
print(f"initialized {self.label}")
def __enter__(self):
print(f"entered {self.label}")
def __exit__(self, exc_type, exc_value, traceback):
print(f"exited {self.label}")
现在我们可以用两种方式测试它(作为装饰器或 context-manager):
if __name__ == "__main__":
def testfunc():
for n in range(10 ** 7):
n ** 0.5
@ctxtdec("decorated")
def decorated():
testfunc()
decorated()
with ctxtdec("square rooting"):
testfunc()
@ctxtdec
def deco2():
testfunc()
deco2()
with ctxtdec:
testfunc()
并且输出:
initialized decorated
entered decorated
exited decorated
initialized square rooting
entered square rooting
exited square rooting
initialized <meta-deco>
entered <meta-deco>
exited <meta-deco>
entered <meta-with>
exited <meta-with>
我想将上下文装饰器与使用或不使用参数的可能性结合起来。
让我们从有参数和无参数的装饰器开始,例如:
import functools
def decorator(func=None, *, label=""):
if func is None:
return functools.partial(decorator, label=label)
@functools.wraps(func)
def wrap(*args, **kwargs):
result = func(*args, **kwargs)
print(f"RESULT {label}: {result}")
return result
return wrap
if __name__ == "__main__":
@decorator(label="with arguments")
def dec_args():
return 1
@decorator
def dec_no_args():
return 0
dec_args()
dec_no_args()
还有 ContextDecorator
可以用作上下文管理器或装饰器:
from contextlib import ContextDecorator
class ctxtdec(ContextDecorator):
def __init__(self, label:str=""):
self.label = label
print(f"initialized {self.label}")
def __enter__(self):
print(f"entered {self.label}")
def __exit__(self, exc_type, exc_value, traceback):
print(f"exited {self.label}")
if __name__ == "__main__":
def testfunc():
for n in range(10 ** 7):
n ** 0.5
@ctxtdec("decorated")
def decorated():
testfunc()
with ctxtdec("square rooting"):
testfunc()
decorated()
但我也希望它也能工作:
@ctxtdec
def decorated():
testfunc()
警告:它并不漂亮,我永远不会真正使用它,但我很好奇所以我让它工作了。可能有人也可以清理一下。
诀窍是让上下文装饰器的 metaclass 成为 ContextDecorator
本身,然后覆盖 __call__
方法检查它是否正在传递标签(正常情况)或函数(paren-less 情况)。
from contextlib import ContextDecorator
class CtxMeta(type, ContextDecorator):
def __enter__(self):
print(f"entered <meta-with>")
def __exit__(self, exc_type, exc_value, traceback):
print(f"exited <meta-with>")
def __call__(cls, func_or_label=None, *args, **kwds):
if callable(func_or_label):
return type.__call__(cls, "<meta-deco>", *args, **kwds)(func_or_label)
return type.__call__(cls, func_or_label, *args, **kwds)
然后,您的原始装饰器 class 与以前保持不变,但添加了元 class 声明:
class ctxtdec(ContextDecorator, metaclass=CtxMeta):
def __init__(self, label:str=""):
self.label = label
print(f"initialized {self.label}")
def __enter__(self):
print(f"entered {self.label}")
def __exit__(self, exc_type, exc_value, traceback):
print(f"exited {self.label}")
现在我们可以用两种方式测试它(作为装饰器或 context-manager):
if __name__ == "__main__":
def testfunc():
for n in range(10 ** 7):
n ** 0.5
@ctxtdec("decorated")
def decorated():
testfunc()
decorated()
with ctxtdec("square rooting"):
testfunc()
@ctxtdec
def deco2():
testfunc()
deco2()
with ctxtdec:
testfunc()
并且输出:
initialized decorated
entered decorated
exited decorated
initialized square rooting
entered square rooting
exited square rooting
initialized <meta-deco>
entered <meta-deco>
exited <meta-deco>
entered <meta-with>
exited <meta-with>