如何判断一个方法是否是装饰器?

3

是否有可能检查一个函数/方法以确定它是否可以用作装饰器?是否按照通常的方式包装其他函数并返回一个可调用对象?具体而言,我要验证第三方代码。


3
你的意思是“可以用作装饰器”还是“在某处被用作装饰器”?如果是前者,通常情况下很难甚至不可能解决,如果是后者,那么实际上是可行的。 - Niklas B.
1
你是在询问函数是否被装饰了吗? - forivall
@NiklasB 抱歉 - 澄清了问题。 - Phillip B Oldham
@unpluggd:鉴于当前情况下这是一项不可能完成的任务,也许如果您告诉我们为什么需要它,我们可以更好地帮助您。 - Niklas B.
5个回答

3

由于Python没有标准化的装饰器,所以除非你知道要查找的装饰器的相关信息,否则没有真正的方法可以判断一个函数是否是装饰器。

如果装饰器在您的控制范围内,您可以添加一个标记来指示它是一个已装饰的函数。否则,没有真正统一的方法来实现这一点。例如:

def decorator(func):
    return g

@decorator
def f()
    pass

def g():
    pass

在上面的例子中,在运行时,f和g将是相同的,没有办法区分这两个。

3
通过应用一个怀疑装饰器,捕获异常,然后测试结果是否包含一个 __call__ 方法,您可以猜测给定的可调用对象是否是装饰器。但这只是一个猜测,而不是保证。
除此之外,我认为在Python语言的动态类型和CPython解释器中内置函数的特殊处理下,你想要的东西通常是不可能实现的。无法以程序方式告知可调用对象将接受另一个可调用对象作为参数,或其返回值的类型。此外,在C中实现的函数中,对于Cpython,您甚至不能检查可调用对象以查看它接受多少个参数。 “装饰器”这个词可以有不同的定义。一种定义方法是,装饰器是任何可调用对象,该对象接受一个单一的(可调用的)参数并返回一个可调用的对象。
请注意,在此定义中,我甚至没有使用“函数”一词;这样做实际上是不正确的。事实上,一些常用的装饰器具有奇怪的属性:
- 内置的classmethodstaticmethod装饰器返回描述符对象,而不是函数。 - 自语言版本2.6以来,您可以装饰类,而不仅仅是函数和方法。 - 包含一个 __init__(self,somecallable)方法和一个 __call__(self,* args,** kwargs)方法的任何类都可以用作装饰器。

谢谢 - 你说装饰器可以是“接受单个可调用参数并返回可调用对象的可调用对象”,这将成为我的测试基础,并足以验证,因为这就是第三方代码中应该创建它们的方式。然而,考虑到复杂性,我只想尝试提供一些漂亮的错误消息。 :D - Phillip B Oldham

2
任何参数正确的可调用对象都可以用作装饰器。请记住:
@foo
def bar(...):

“exactly”与“完全相同”意思相同。

def bar(...):
   ...
bar = foo(bar)

自然而然地,由于foo可能返回任何东西,你无法检查函数是否已被装饰。虽然foo可以很好地留下标记,但它没有这样做的义务。


如果您有一些 Python 代码,并且想找出所有的装饰器,可以将代码解析为抽象语法树,然后遍历该树以查找已装饰的函数。以下是一个示例,存储装饰器的.id。显然,如果需要,您也可以存储ast对象。

>>> class DecoratorFinder(ast.NodeVisitor):
...     def __init__(self, *args, **kwargs):
...         super(DecoratorFinder, self).__init__(*args, **kwargs)
...         self.decorators = set()
...     
...     def visit_FunctionDef(self, node):
...         self.decorators.update(dec.id for dec in node.decorator_list)
...         self.generic_visit(node)
... 
>>> finder = DecoratorFinder()
>>> x = ast.parse("""
... @dec
... def foo():
...     pass
... """)
>>> finder.visit(x)
>>> finder.decorators
set(['dec'])

实际上,只有至少需要一个位置参数的可调用对象才是可能的... - Niklas B.

1

不,这是不可能的。也许你应该思考一下为什么需要检查f是否是装饰器,而不是检查它是否是装饰器。

如果你期望某些特定的装饰器,你可以直接检查它们;如果你想要某些特定的行为/方法/属性,你可以检查它们。

如果你想要检查某个可调用的f是否可以用作装饰器,你可以通过传递一些虚拟函数来测试装饰器的行为,但通常情况下它可能无法正常工作或对不同的输入具有不同的行为。

以下是一个这样的简单检查:

def decorator1(func):
    def _wrapper(*args, **kwargs):
        print "before"
        func(*args, **kwargs)
        print "after"

    return _wrapper

def dummy_func(): pass

out_func = decorator1(dummy_func)

if callable(out_func) and dummy_func != out_func:
    print "aha decorated!"

你不能直接通过传递一些虚拟函数来检查它...因为你无法确定它会对其参数执行什么操作! - Katriel
@katrielalex 是的,这就是为什么一般的答案是否定的,但如果你知道装饰器应该做什么,你可能可以通过使用它来检查行为。 - Anurag Uniyal

0

我从未做过这样的事情,但通常情况下,Python 在这种情况下依赖于“鸭子类型”。因此,您可以尝试装饰一个虚拟函数并查看是否返回可调用对象。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接