python 检查函数是否被修饰

wtzytmuj  于 2023-10-14  发布在  Python
关注(0)|答案(4)|浏览(159)

我试图在一个类方法中构建一个控制结构,它接受一个函数作为输入,并且如果一个函数被装饰或没有装饰,它就有不同的行为。关于如何构建一个行为如下的函数is_decorated,有什么想法吗?

def dec(fun):
    # do decoration

def func(data):
    # do stuff

@dec
def func2(data):
    # do other stuff

def is_decorated(func):
    # return True if func has decorator, otherwise False

is_decorated(func)  # False
is_decorated(func2) # True
eqqqjvef

eqqqjvef1#

是的,这相对容易,因为函数可以添加任意的属性,所以装饰器函数可以在它做它的事情时添加一个:

def dec(fun):
    def wrapped(*args, **kwargs):
        pass

    wrapped.i_am_wrapped = True
    return wrapped

def func(data):
    ... # do stuff

@dec
def func2(data):
    ... # do other stuff

def is_decorated(func):
    return getattr(func, 'i_am_wrapped', False)

print(is_decorated(func))   # -> False
print(is_decorated(func2))  # -> True
ymzxtsji

ymzxtsji2#

有几种方法可以识别 * 函数**是否被 Package /修饰。

TLDR解决方案

def is_decorated(func):
    return hasattr(func, '__wrapped__') or func.__name__ not in globals()

情景A

如果我们假设装饰器使用辅助装饰器functools.wraps,那么它非常简单,因为我们的装饰函数将在后面具有属性__wrapped__

from functools import wraps

def decorator(function):
    @wraps(function)
    def _wrapper(*a, **kw): ...
    return _wrapper

@decorator
def foo(x, y): ...

def is_decorated(function):
    return hasattr(function, '__wrapped__')

情景B

如果我们在decorator的定义中没有使用wraps,那么它就有点不同了。

def decorator(function):
    def _wrapper(*a, **kw): ...
    return _wrapper

@decorator
def foo(x, y): ...

def bar(x, y): ...

print(bar.__name__)  # prints 'bar'
print(foo.__name__)  # prints '_wrapper' instead of 'foo'

def is_decorated(function):
    """
    globals() returns a dictionary which includes
    defined functions, classes and many other stuff.
    Assuming we haven't defined the inner/wrapper name
    as an outer function elsewhere, then it will not be
    in globals()
    """
    return function.__name__ not in globals()

结论

还有其他更复杂的方法来检查这一点,然而可读性和完整性一样重要,这个解决方案尽可能简单。正如前面的代码块中所提到的,这个解决方案的唯一“漏洞”是以下情况:

from functools import wraps

def is_decorated(func):
    return hasattr(func, '__wrapped__') or func.__name__ not in globals()

def decorator_wraps(function):
    @wraps(function)
    def _wrapper(*a, **kw): ...
    return _wrapper

def decorator_no_wraps(function):
    def _wrapper(*a, **kw): ...
    return _wrapper

@decorator_wraps
def foo(x, y): ...

@decorator_no_wraps
def bar(x, y): ...

def baz(x, y): ...

for function in (foo, bar, baz):
    print(is_decorated(function))  # True, True, False

def _wrapper(): ...

for function in (foo, bar, baz):
    print(is_decorated(function))  # True, False, False
q9rjltbz

q9rjltbz3#

如果利用基于类的装饰器呢?使用__call__方法使类成为可调用类,就像它是一个函数一样。

class dec:
    def __init__(self, func):
        self.wrapped_func = func

    def __call__(self, *args, **kwargs):
        return self.wrapped_func(*args, **kwargs)
   
def func(data):
    ...

@dec
def func2(data):
    ...

def is_decorated(func):
    return isinstance(func, dec)

assert is_decorated(func) is False
assert is_decorated(func2) is True
ddhy6vgd

ddhy6vgd4#

一般来说,你不能。会留下痕迹的装修师没什么特别的。根据优秀的Primer on Python Decorators教程,“装饰器为调用higher-order functions提供了一个简单的语法”。
本教程中的一个快速说明性示例:

def do_twice(func):
    def wrapper_do_twice():
        func()
        func()
    return wrapper_do_twice
from decorators import do_twice

@do_twice
def say_whee():
    print("Whee!")

你看到do_twice内部的wrapper_do_twice实际上只是调用了传递的函数两次了吗?它绝不会修改函数,所以不可能告诉(没有inspection,它不会这样做)函数被修饰了。
然而,如果你正在编写自己的装饰器,或者你知道你可以利用你正在使用的装饰器修改装饰函数(与上面的例子相反,它不修改函数),那么你可以直接检查剩下的任何标记。请参阅this问题的示例。

相关问题