【问题标题】:How to deal with limitations of "inspect.getsource" - or how to get ONLY the source of a function?如何处理“inspect.getsource”的限制 - 或如何仅获取函数的来源?
【发布时间】:2019-03-11 17:19:23
【问题描述】:

我一直在使用 Python 标准库中的 inspect 模块。

以下示例可以正常工作(假设 inspect 已导入):

def foo(x, y):
    return x - y
print(inspect.getsource(foo))

... 将打印 def foo(x, y):\n return x - y\n 和 ...

bar = lambda x, y: x / y
print(inspect.getsource(bar))

... 将打印bar = lambda x, y: x / y\n。到现在为止还挺好。然而,在以下示例中,事情变得有些奇怪:

print(inspect.getsource(lambda x, y: x / y))

... 将打印 print(inspect.getsource(lambda x, y: x / y)) 和 ...

baz = [2, 3, lambda x, y: x / y, 5]
print(inspect.getsource(baz[2]))

...将打印baz = [2, 3, lambda x, y: x / y, 5]

模式似乎是所有相关的源代码,无论上下文如何,都由getsource 返回。这些线上的所有其他内容,在我的情况下,除了所需的函数源/定义之外的东西,也包括在内。是否有另一种“替代”方法,可以提取代表函数源代码的内容 - 并且只提取其源代码 - 最好以某种匿名方式?


编辑(1)

def foo(x, y):
    return x - y
bar = [1, 2, foo, 4]
print(inspect.getsource(bar[2]))

...将打印def foo(x, y):\n return x - y\n

【问题讨论】:

  • @cglacet 感谢您的想法。我查看了dill 及其源代码并对其进行了测试。在我的问题的上下文中,它是对inspect.getsource 的略微改进的包装器,涵盖了一些边缘情况,例如在交互式环境中定义的函数。但它并没有解决我的根本问题——这里没有区别。
  • 你“只”需要捕获 lambdas 吗?如果是,那么也许可以编写一个正则表达式来从代码行中提取 lambda。但我想这可能有点复杂。
  • @cglacet 我正在寻找适用于任何类型的函数定义/函数指针/函数引用的通用解决方案。似乎 lambdas 是更复杂的边缘情况......def 的函数定义更容易。例如,如果我在列表中引用了“真正的”Python 函数,类似于我上面的 baz[2] 示例,inspect 确实会提供函数的源而不是列表的定义。
  • 你上面的例子没有给出定义列表的行,而是出现第 2 项的行

标签: python python-3.x inspect


【解决方案1】:

不幸的是,inspect 无法做到这一点,而且如果不再次解析(和编译)源代码,它也不太可能工作。 inspectgetsource 方法相当有限:它使用getsourcelines 调用然后findsource,这基本上解开了您的对象,直到我们最终到达PyCodeObject

此时,我们正在处理已编译的字节码。原始来源 are fragments and hints, such 中剩下的所有内容为 co_firstlineno

/* Bytecode object */
typedef struct {
    /* ... other fields omitted ... */
    int co_firstlineno;         /* first source line number */
    PyObject *co_code;          /* instruction opcodes */
    /* ... other fields omitted ... */
} PyCodeObject;

顺便说一句,类似于PyCodeObject,一个PyFrameObject also contains only a f_lineno,但是没有列,这就解释了为什么回溯只包括文件名和行:列不是编译成字节码。

由于字节码不包含比(第一)行更多的特定区域,因此无法从 inspect 或任何其他仅使用(公共)字节码信息而无需进一步解析的库中获取确切的源位置。这也适用于仅使用字节码的任何其他选项,例如 pickle

inspect 使用公共信息 (co_firstlineno) 和then just searches for a suitable begin of a function and the end of the surrounding block。然而,inspect几乎在那里,但它只找到any block, not the correct one,并且目前无法找到正确的。 inspect 标记整行并且没有从正确的变体开始,它也不知道正确的对应源代码区域。

假设我们有

plus, minus, mult = lambda x: x + 1, lambda y: y - 1, lambda z: z * 5

我们只想要minus。由于字节码不包含co_firstcolumn,我们只有完整的行可用。我们可以解析所有的 lambda,但我们仍然不知道哪个 lambda 适合我们的co_code。我们需要再次编译它们并检查它们的字节码是否与原始字节码相符。

最后,我们必须这样做:再次解析源代码并找到正确的PyCodeObject。如果我们至少有一个起始列号会容易得多,因为我们可以只使用语法分析,但 AST 只保留line numbers at the moment。所以要么inspect需要一个大补丁,要么字节码需要包含编译对象的起始列。

【讨论】:

    猜你喜欢
    • 2017-11-28
    • 1970-01-01
    • 2018-09-26
    • 2020-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多