Is it possible to check if a function is decorated inside another function?
在我的例子中,如果调用一个函数(这里的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | def out(fun): def inner(*args, **kwargs): fun(*args, **kwargs) return inner @out def decorated(): f1() def not_decorated(): f1() def f1(): if is_decorated_by_out: # here I want to check it print('I am') else: print('I am not') decorated() not_decorated() |
预期输出:
1 2 | I am I am not |
很明显,这是一个骇客,所以我不建议这样做,但是由于您已经排除了附加参数,而且无论包装与否,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import inspect def out(fun): def inner(*args, **kwargs): __wrapped_by__ = out fun(*args, **kwargs) return inner def is_wrapped_by(func): try: return inspect.currentframe().f_back.f_back.f_back.f_locals.get('__wrapped_by__') is func except AttributeError: return False @out def decorated(): f1() def not_decorated(): f1() def f1(): if is_wrapped_by(out): print('I am') else: print('I am not') decorated() not_decorated() |
尝试在线!
这假设了特定程度的嵌套(通过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | def is_wrapped_by(func): frame = None try: # Skip is_wrapped_by and caller frame = inspect.currentframe().f_back.f_back while True: if frame.f_locals.get('__wrapped_by__') is func: return True frame = frame.f_back except AttributeError: pass finally: # Leaving frame on the call stack can cause cycle involving locals # which delays cleanup until cycle collector runs; # explicitly break cycle to save yourself the headache del frame return False |
假设你有一个像这样的功能装饰
1 2 3 4 | def double_arg(fun): def inner(x): return fun(x*2) return inner |
但是,您不能访问它(它在第三方库或其他地方)。在这种情况下,您可以将它包装成另一个函数,该函数将装饰的名称添加到生成的函数中。
1 2 3 4 5 6 | def keep_decoration(decoration): def f(g): h = decoration(g) h.decorated_by = decoration.__name__ return h return f |
用包装纸代替旧的装饰。
1 | double_arg = keep_decoration(double_arg) |
甚至可以编写一个助手函数来检查函数是否被修饰。
1 2 3 4 5 | def is_decorated_by(f, decoration_name): try: return f.decorated_by == decoration_name except AttributeError: return False |
使用示例…
1 2 3 4 5 6 7 8 9 10 11 12 | @double_arg def inc_v1(x): return x + 1 def inc_v2(x): return x + 1 print(inc_v1(5)) print(inc_v2(5)) print(is_decorated_by(inc_v1, 'double_arg')) print(is_decorated_by(inc_v2, 'double_arg')) |
产量
1 2 3 4 | 11 6 True False |
如果您愿意在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import functools def out(fun): @functools.wraps(fun) def inner(*args, **kwargs): fun(*args, **kwargs) return inner @out def decorated(): f1(decorated) def not_decorated(): f1(not_decorated) def f1(_func): if getattr(_func, '__wrapped__', False): print('I am') else: print('I am not') decorated() not_decorated() |
输出:
1 2 | I am I am not |