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()
中创建新参数。这是一个很大的障碍。