Python 是否可以检查一个函数是否在另一个函数中修饰?
在我的示例中,如果调用一个函数(此处Python 是否可以检查一个函数是否在另一个函数中修饰?,python,python-decorators,Python,Python Decorators,在我的示例中,如果调用一个函数(此处修饰的或未修饰的)有一个特定的修饰符(in-code@out),有没有办法检查内部函数f1?这些信息是否传递给函数 def out(fun): def inner(*args, **kwargs): fun(*args, **kwargs) return inner @out def decorated(): f1() def not_decorated(): f1() def f1(): if i
修饰的或未修饰的)有一个特定的修饰符(in-code@out
),有没有办法检查内部函数f1
?这些信息是否传递给函数
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()
预期产出:
I am
I am not
如果您愿意在f1
中创建其他参数(也可以使用默认参数),则可以使用functools.wrapps
并检查\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>属性是否存在。为此,将包装器函数传递给f
:
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()
输出:
I am
I am not
假设你有一个像这样的功能装饰
def double_arg(fun):
def inner(x):
return fun(x*2)
return inner
但是,您无法访问它(它位于第三方库或其他内容中)。在这种情况下,您可以将其包装到另一个函数中,该函数将装饰的名称添加到结果函数中
def keep_decoration(decoration):
def f(g):
h = decoration(g)
h.decorated_by = decoration.__name__
return h
return f
用包装纸代替旧的装饰
double_arg = keep_decoration(double_arg)
您甚至可以编写一个helper函数来检查函数是否被修饰
def is_decorated_by(f, decoration_name):
try:
return f.decorated_by == decoration_name
except AttributeError:
return False
使用示例
@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'))
输出
11
6
True
False
要明确的是,这是骇人听闻的黑客行为,所以我不建议这样做,但由于您排除了其他参数,而且无论是否包装,f1
都将是相同的,因此您将黑客作为唯一的选择。解决方案是在包装器函数中添加一个局部变量,其唯一目的是通过堆栈检查找到:
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()
这假设了特定程度的嵌套(通过f_back
来解释的手动回溯是由本身包裹的,f1
,装饰的
,最后到内部
(从向外
)。如果要确定调用堆栈中的任何位置是否涉及out
,请使用循环对进行包装,直到堆栈耗尽:
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
您可以将属性添加到函数out()
返回中,该函数的存在可以签入f1()
。让函数尝试检查调用该函数的代码的所有内容通常是最后的手段-您应该尝试找到其他方法将必要的信息输入到函数中,或者重新考虑是否确实需要它。我将
中的
更改为
内部
中的
是保留字,因此调用或返回<在
中的code>不起作用。理论上,您的装饰程序可以为正在装饰的函数设置属性。主要问题是函数不能引用自身(因此您无法检查f1
中是否有该属性),您需要另一个装饰程序。请参阅“装饰”函数本身不会留下任何痕迹。@out def dedecorded()…
只是def decorded():…;decorded=out(decorded)
的语法糖。这是一个很好的解决方案,但我无法在f()
中创建新参数。这是一个很大的障碍。