如何调试动态定义的Python函数?
有没有办法调试一个在运行时动态定义的函数呢?
或者至少有没有简单的方法可以找到这个函数是怎么产生的?
更新以提供更多细节:
我使用了 inspect
模块:
ipdb> inspect.getmodule(im.get_thumbnail_url)
Out[0]: <module 'django.utils.functional' from 'C:\java\python\Python25\Lib\site
-packages\django\utils\functional.pyc'>
ipdb> inspect.getsource(im.get_thumbnail_url)
Out[0]: ' def _curried(*moreargs, **morekwargs):\n return _curried_fun
c(*(args+moreargs), **dict(kwargs, **morekwargs))\n'
这里 inspect
显示,photos.models.Image
类中的 get_thumbnail_url
方法是由 django.utils.functional.curry._curried
函数生成的。但它仍然没有显示这个方法是在哪里产生的,也就是 _curried
函数是在哪里被调用的。这些信息对于了解 get_thumbnail_url
是怎么实现的非常重要。
我可以在 _curried
函数里面放 pdb
,但是这样会经常中断,因为这个函数调用非常频繁。我需要一些特别的特征来使用断点条件。
关于解决方案的更新:
感谢大家的建议。我找到了解决方案。让我解释一下我是怎么找到的,或许能帮助到其他人:
首先,我在 pinax 源代码中搜索了 'get_thumbnail_url' 这个词。没有结果。
然后,我在 pinax 源代码中搜索了 'thumbnail' 这个词。也没有有用的结果。
最后,我在 pinax 源代码中搜索了 'curry' 这个词。以下是几个结果之一:
def add_accessor_methods(self, *args, **kwargs):
for size in PhotoSizeCache().sizes.keys():
setattr(self, 'get_%s_size' % size,
curry(self._get_SIZE_size, size=size))
setattr(self, 'get_%s_photosize' % size,
curry(self._get_SIZE_photosize, size=size))
setattr(self, 'get_%s_url' % size,
curry(self._get_SIZE_url, size=size))
setattr(self, 'get_%s_filename' % size,
curry(self._get_SIZE_filename, size=size))
get_thumbnail_url
方法是通过这个调用生成的:curry(self._get_SIZE_url, size=size))
。
但当然这不是一个通用的解决方法。如果你能分享其他找到动态定义函数实际产生位置的方法,那将非常有帮助。
编辑:
最佳的 通用解决方案 是由 Jason Orendorff 写的。
2 个回答
不太确定,不过你可以用一个叫做inspect的模块来获取一个函数的定义位置,包括它所在的文件和行号:
import inspect
f = lambda: inspect.currentframe().f_code.co_filename + ':' + \
str(inspect.currentframe().f_lineno);
print f()
它会打印出:
script.py:2
给定一个函数 f
,在 CPython 中,你可以通过打印 f.func_code.co_filename
和 f.func_code.co_firstlineno
来获取这个函数所在的文件和它的第一行行号。不过,如果这个函数是通过 eval
或 exec
创建的,这个方法就没用了。
错误追踪信息中也包含文件和行号的信息。
如果你 import dis
,你可以使用 dis.dis(f)
来查看 CPython 的字节码;这可能不是特别有用,但有可能显示一些字符串,帮助你找到正确的位置。
另一个可以查看的工具是 PDB,这是 Python 的文本模式调试器。在出现异常后,使用 import pdb; pdb.pm()
可以启动 PDB。虽然它比较原始,但还是很有用的;你可以输入 help
来查看命令列表。where
命令可以显示调用栈。
编辑:你提到这是一个柯里化函数。如果它是通过 functools.partial
创建的,那么它会有一个 .func
属性,指向底层的函数。
编辑 2:一般的方法是在 im.get_thumbnail_url
上设置一个断点,然后立即调用它。你的断点应该会立刻被触发。然后逐步执行,直到到达你感兴趣的代码。
因为这个柯里化函数是通过类似下面的代码生成的:
def curry(_curried_func, *args, **kwargs):
def _curried(*moreargs, **morekwargs):
return _curried_func(*(args+moreargs), **dict(kwargs, **morekwargs))
return _curried
另一种方法是检查 f.func_closure
,像这样:
>>> f
<function _curried at 0xb77d64fc>
>>> f.func_closure
(<cell at 0xb77eb44c: tuple object at 0xb77dfa0c>, <cell at 0xb77eb5e4: dict object at 0xb77d93e4>, <cell at 0xb77eb5cc: function object at 0xb77d1d84>)
这个函数闭包包含三个变量:一个元组、一个字典和一个函数。显然,我们感兴趣的是那个函数(顺便说一下,这三个单元对应于在外部函数 curry
中定义的变量 args
、kwargs
和 _curried_func
,而这些变量被闭包 _curried
使用)。
>>> f.func_closure[2].cell_contents
<function say at 0xb77d1d84>
>>> import inspect
>>> inspect.getsource(_)
'def say(x):\n print x\n'