是否可以检查一个功能是否在另一个功能中修饰?

时间:2018-12-13 19:46:06

标签: python python-decorators

在我的示例中,如果调用函数(此处为f1decorated)具有特定的修饰符(在代码not_decorated中),是否可以检查函数@out内的任何方法?这样的信息是否传递给函数?

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

3 个答案:

答案 0 :(得分:2)

要清楚,这是骇人听闻的骇客,所以我不推荐这样做,但是由于您已经排除了其他参数,并且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()

Try it online!

这假定了特定的嵌套程度(通过f_back进行手动回溯以说明is_wrapped_by本身,f1decorated,最后是{{1} }(来自inner)。如果要确定out是否在调用堆栈的任何地方都涉及到,请进行out循环直到堆栈用尽: / p>

is_wrapped_by

答案 1 :(得分:0)

如果您愿意在f1中创建其他参数(也可以使用默认参数),则可以使用functools.wraps并检查是否存在__wrapped__属性。为此,请将包装函数传递给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

答案 2 :(得分:0)

假设您具有这样的功能修饰

def double_arg(fun):
    def inner(x):
        return fun(x*2)
    return inner

但是您无法访问它(它在第3方库中或其他内容中)。在这种情况下,您可以将其包装到另一个将装饰名称添加到结果函数中的函数中

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)

您甚至可以编写一个辅助函数来检查该函数是否被修饰。

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