检查函数是否被修饰

问题描述 投票:0回答:4

我正在尝试在类方法中构建一个控制结构,该方法将函数作为输入,并且如果函数被修饰或未修饰,则具有不同的行为。关于如何构建一个行为如下的函数

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
python python-3.x closures decorator python-decorators
4个回答
3
投票

是的,这相对容易,因为函数可以添加任意属性,因此装饰器函数在执行其操作时可以添加一个:

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

1
投票

有多种方法可以识别函数是否被包装/装饰。

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

如果我们在装饰器定义期间不使用

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

0
投票

如果尝试基于类的装饰器会怎么样?使用

__call__
方法使类成为可调用的类,就像它是一个函数一样。在这里,我们使用
Decorator
类及其后代
dec
并查找该函数是否被装饰或使用了某些特定的装饰器。

class Decorator:
    pass

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

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


def func(data):
    pass


@dec
def func2(data):
    pass


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


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


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

-1
投票

一般来说,你不能。留下痕迹的装饰者并没有什么特别的。根据出色的 Python 装饰器入门 教程,“装饰器提供了一种用于调用 高阶函数 的简单语法。”

教程中的一个快速说明性示例:

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!")

您看到

wrapper_do_twice
内部的
do_twice
实际上只是调用传递的函数两次吗?它绝不会修改函数,因此不可能知道(没有 inspection,不要这样做)函数已被修饰。

但是,如果您正在编写自己的自己的装饰器,或者知道您可以利用您正在使用的装饰器正在修改被装饰的函数(与上面的示例相反,上面的示例修改函数),然后你可以直接检查是否留下了任何标记。有关示例,请参阅this问题。

© www.soinside.com 2019 - 2024. All rights reserved.