我正在编写一个装饰器,并且由于各种恼人的原因[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 …>: False
<function bar …>: True
另外,请注意:
typeof
或inspect
)将不起作用。 [0]:具体来说,我正在编写一个装饰器,可以很容易地使用nose
进行参数化测试。但是,nose
将不在unittest.TestCase
的子类上运行测试生成器,所以我希望我的装饰器能够确定它是否在{{1}的子类中使用并且失败并出现适当的错误。显而易见的解决方案 - 在调用包装函数之前使用TestCase
不起作用,因为包装函数需要成为一个生成器,它根本不会被执行
答案 0 :(得分:13)
在包装方法时,请查看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
,但我找不到任何方法来学习在类创建完成时将附加哪些超类。
答案 1 :(得分: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
参数。
答案 2 :(得分: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。
答案 3 :(得分:1)
您可以检查装饰器本身是在模块级别调用还是嵌套在其他内容中。
defined_in_class = inspect.currentframe().f_back.f_code.co_name != "<module>"
答案 4 :(得分:1)
您可以使用软件包wrapt
进行检查
-实例/类方法
-课程
-独立的函数/静态方法:
请参见wrapt
的项目页面:https://pypi.org/project/wrapt/
答案 5 :(得分:-2)
我认为inspect
模块中的功能可以满足您的需求,尤其是isfunction和ismethod:
>>> 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