如何查看方法是否为装饰器?

时间:2012-03-20 21:18:44

标签: python decorator introspection

是否可以检查函数/方法以查看它是否可以用作装饰器?因为它遵循通常的方式装饰器包装其他函数并返回一个可调用的?具体来说,我希望验证第三方代码。

5 个答案:

答案 0 :(得分:2)

由于Python中没有标准化的装饰器,除非你对你正在寻找的装饰器有所了解,否则没有真正的方法来判断一个函数是否是装饰器。

如果装饰者在你的控制之下,你可以添加一个标记来表明它是一个装饰的功能。否则,没有真正统一的方法来做到这一点。以这个例子为例:

def decorator(func):
    return g

@decorator
def f()
    pass

def g():
    pass

在上面的例子中,在运行时,f和g将是相同的,并且无法将两者分开。

答案 1 :(得分:2)

通过应用可疑的装饰器,捕获异常,然后测试结果是否包含__call__方法,您可以猜测给定的可调用者是否是装饰者。但这只是猜测,而不是保证。

除此之外,由于Python语言的动态类型特性以及CPython解释器中内置函数的特殊处理,我不相信你想要的一般性。无法以编程方式判断可调用对象是否接受另一个可调用对象作为参数,或者其返回值将具有哪种类型。此外,在CPython中,对于在C中实现的函数,您甚至无法检查可调用对象以查看它接受的参数数量。

“装饰者”这个词可以用来表示不同的东西。定义它的一种方法是,装饰器是任何可调用的,它接受一个(可调用的)参数并返回一个可调用的。

请注意,我在这个定义中甚至没有使用“功能”这个词;这样做实际上是不正确的。实际上,一些常用的装饰器具有奇怪的属性:

  • 内置classmethodstaticmethod装饰器返回描述符对象,而不是函数。
  • 从语言版本2.6开始,您可以装饰类,而不仅仅是函数和方法。
  • 任何包含__init__(self, somecallable)方法和__call__(self, *args, **kwargs)方法的类都可以用作装饰器。

答案 2 :(得分:1)

任何具有正确数量参数的可调用都可以用作装饰器。请记住

@foo
def bar(...):

完全相同
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'])

答案 3 :(得分:0)

我从来没有做过这样的事情,但一般来说python在这种情况下依赖于“duck-typing”。所以你可以尝试装饰一个虚函数,看看是否返回了一个callable。

答案 4 :(得分:0)

不,这是不可能的。可能不是要检查f是否是装饰者,你应该考虑为什么需要检查它?

如果你期待一些特定的装饰,你可以直接检查,如果你想要一些特定的行为/方法/属性,你可以检查

如果你想检查一些可调用的f是否可以用作装饰器,你可以通过传递一些虚函数来测试装饰器的行为,但一般来说它可能不起作用或对不同的输入有不同的行为。 / p>

这是一个如此天真的检查:

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