2012-01-09 199 views
12

我正在写一个装饰器,并且出于各种令人讨厌的原因[0],检查它正在包装的函数是单独定义还是作为类的一部分来定义(以及哪些类是新类是子类)。Python:装饰器可以确定一个函数是否正在类中定义?

例如:

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 …>: False 
<function bar …>: True 

另外,请注意:

  • 在点装饰应用的功能将仍然是一个函数,而不是一个未绑定的方法,因此测试实例/未绑定方法(使用typeofinspect)将不会工作。
  • 请只提供该解决这个问题的建议 - 我知道,有很多类似的方法来达到这个目的(例如,使用一个类装饰),但我想他们在装饰时有发生,不晚了。

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

+0

为了好奇,这里是结果:http://paste.pocoo.org/show/532430/ – 2012-01-09 20:23:52

回答

11

看看的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 ,但我无法找到任何方式来了解课程创建完成后将要附加的超类。

-2

我觉得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 

然后,您可以按照Types and Members table访问结合或未结合的方法中的功能:

>>> C.foo.im_func 
<function foo at 0x1062dfaa0> 
>>> inspect.isfunction(C.foo.im_func) 
True 
>>> inspect.ismethod(C.foo.im_func) 
False 
+0

我期望'inspect'会被涉及,但不是你描述的方式。看到我的第一个注释(在应用装饰器时,函数只是'function'的一个实例,而不是'unboundmethod'或'boundmethod')。 – 2012-01-09 18:37:52

+0

他没有明确说明,但他的观点是这不适用于课堂定义时间。 – 2012-01-09 18:38:03

+0

是的,你是对的 - 我已经更新了我的问题,以便更明确。我很感谢彻底的回答,但它只是没有解决我的问题。 – 2012-01-09 18:41:00

2

我有一些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参数。

1

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

defined_in_class = inspect.currentframe().f_back.f_code.co_name != "<module>" 
+0

Hrm ...但是这对嵌套在类内的类不起作用,会吗? (例如,'def mk_class():class MyClass:...; return MyClass') – 2012-01-09 19:32:16

+0

函数执行或返回的内容不会影响'inspect.currentframe()。f_back'的值。唯一重要的是'my_decorator'被调用的地方,而且它总是与定义它的东西的层次相同。 – chepner 2012-01-09 22:04:13

+0

我可能误解了你的评论;在调用封闭函数之前,其他函数中的任何装饰函数都不会运行装饰器,但是将会正确计算包装函数的作用域。您可以在装饰器中使用它来查看装饰器运行时的封闭范围列表:'inspect.stack()[1:]]'中的x [[x] .f_code.co_name]。 – chepner 2012-01-09 22:37:51

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。

相关问题