如何处理 "inspect.getsource" 的限制 - 或者如何仅获取函数的源代码?
How to deal with limitations of "inspect.getsource" - or how to get ONLY the source of a function?
我一直在玩 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
返回所有相关源代码 行 而不管上下文如何。这些行中的所有其他内容,在我的例子中,除了所需的函数源/定义之外,也包括在内。是否有另一种 "alternative" 方法,允许提取代表函数源代码的内容 - 并且仅提取其源代码 - 最好以某种匿名方式?
编辑 (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
.
不幸的是,inspect
无法做到这一点,而且如果不再次解析(和编译)源代码就不可能工作。 inspect
的 getsource
method is rather limited: it uses getsourcelines
to call then findsource
, which essentially unwraps your object until we end up at a 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
或任何其他仅使用 (public) 字节码信息,无需进一步解析。这也适用于任何其他仅使用字节码的选项,例如 pickle
.
inspect
使用 public 信息 (co_firstlineno
) 和 then just searches for a suitable begin of a function and the end of the surrounding block. However, inspect
is almost there, but it only finds 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
需要大补丁,要么字节码需要包含编译对象的起始列。
我一直在玩 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
返回所有相关源代码 行 而不管上下文如何。这些行中的所有其他内容,在我的例子中,除了所需的函数源/定义之外,也包括在内。是否有另一种 "alternative" 方法,允许提取代表函数源代码的内容 - 并且仅提取其源代码 - 最好以某种匿名方式?
编辑 (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
.
不幸的是,inspect
无法做到这一点,而且如果不再次解析(和编译)源代码就不可能工作。 inspect
的 getsource
method is rather limited: it uses getsourcelines
to call then findsource
, which essentially unwraps your object until we end up at a 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
或任何其他仅使用 (public) 字节码信息,无需进一步解析。这也适用于任何其他仅使用字节码的选项,例如 pickle
.
inspect
使用 public 信息 (co_firstlineno
) 和 then just searches for a suitable begin of a function and the end of the surrounding block. However, inspect
is almost there, but it only finds 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
需要大补丁,要么字节码需要包含编译对象的起始列。