15

我正在编写一个装饰器,出于各种烦人的原因[0],检查它所包装的函数是独立定义还是作为类的一部分定义(以及新类是子类化的类)是方便的.

例如:

def my_decorator(f):
    defined_in_class = ??
    print "%r: %s" %(f, defined_in_class)

@my_decorator
def foo(): pass

class Bar(object):
    @my_decorator
    def bar(self): pass

应该打印:

<function foo …&gt;: False
<function bar …&gt;: True

另外,请注意:

  • 在应用装饰器时,该函数仍然是一个函数,而不是一个未绑定的方法,因此测试实例/未绑定的方法(使用typeofor inspect)将不起作用。
  • 请只提供解决这个问题的建议——我知道有很多类似的方法可以实现这个目的(例如,使用类装饰器),但我希望它们在装饰时发生,而不是稍后。

[0]:具体来说,我正在编写一个装饰器,可以很容易地使用nose. 但是,nose不会在 的子类运行测试生成器unittest.TestCase,所以我希望我的装饰器能够确定它是否在 的子类中使用TestCase并失败并出现适当的错误。显而易见的解决方案 -isinstance(self, TestCase)在调用包装函数之前使用不起作用,因为包装函数需要是一个生成器,它根本不会被执行。

4

6 回答 6

15

inspect.stack()查看包装方法时的输出。当您的装饰器正在执行时,当前堆栈帧是对您的装饰器的函数调用;下一个堆栈帧是@应用于新方法的包装动作;第三帧将是类定义本身,它值得一个单独的堆栈帧,因为类定义是它自己的命名空间(它在执行完成时被包装以创建一个类)。

因此,我建议:

defined_in_class = (len(frames) > 2 and
                    frames[2][4][0].strip().startswith('class '))

如果所有这些疯狂的索引看起来都无法维护,那么您可以通过将框架逐块拆开来更加明确,如下所示:

import inspect
frames = inspect.stack()
defined_in_class = False
if len(frames) > 2:
    maybe_class_frame = frames[2]
    statement_list = maybe_class_frame[4]
    first_statment = statement_list[0]
    if first_statment.strip().startswith('class '):
        defined_in_class = True

请注意,在您的包装器运行时,我看不到任何方法可以向 Python 询问类名或继承层次结构。这一点在处理步骤中“为时过早”,因为类创建尚未完成。要么解析以你自己开头的行,class然后在该框架的全局变量中查找超类,要么在frames[1]代码对象周围查看你能学到什么——看起来类名最终出现frames[1][0].f_code.co_name在上面的代码中,但我找不到任何方法来了解类创建完成后将附加哪些超类。

于 2012-01-09T19:01:14.730 回答
2

在这里聚会有点晚了,但这已被证明是确定装饰器是否用于类中定义的函数的可靠方法:

frames = inspect.stack()

className = None
for frame in frames[1:]:
    if frame[3] == "<module>":
        # At module level, go no further
        break
    elif '__module__' in frame[0].f_code.co_names:
        className = frame[0].f_code.co_name
        break

这种方法相对于公认答案的优势在于它适用于例如 py2exe。

于 2013-08-27T17:52:16.240 回答
1

我有一些hacky解决方案:

import inspect

def my_decorator(f):
    args = inspect.getargspec(f).args
    defined_in_class = bool(args and args[0] == 'self')
    print "%r: %s" %(f, defined_in_class)

但它依赖self于函数中参数的存在。

于 2012-01-09T19:00:08.993 回答
1

您可以使用该包wrapt检查
- 实例/类方法
- 类
- 独立函数/静态方法:

查看项目页面wrapthttps ://pypi.org/project/wrapt/

于 2019-07-03T09:33:07.720 回答
0

您可以检查装饰器本身是在模块级别调用还是嵌套在其他内容中。

defined_in_class = inspect.currentframe().f_back.f_code.co_name != "<module>"
于 2012-01-09T19:04:42.540 回答
-3

我认为inspect模块中的函数会做你想做的事,尤其是isfunctionismethod

>>> import inspect
>>> def foo(): pass
... 
>>> inspect.isfunction(foo)
True
>>> inspect.ismethod(foo)
False
>>> class C(object):
...     def foo(self):
...             pass
... 
>>> inspect.isfunction(C.foo)
False
>>> inspect.ismethod(C.foo)
True
>>> inspect.isfunction(C().foo)
False
>>> inspect.ismethod(C().foo)
True

然后,您可以按照类型和成员表访问绑定或未绑定方法中的函数:

>>> C.foo.im_func
<function foo at 0x1062dfaa0>
>>> inspect.isfunction(C.foo.im_func)
True
>>> inspect.ismethod(C.foo.im_func)
False
于 2012-01-09T18:35:26.393 回答