为什么检查会为从超类继承的类返回不同的行?



在试图确定函数是否使用@decorator语法调用时,我们意识到inspect在查看从超类继承的装饰类时具有不同的行为。

在Windows 10下的CPython 3.6.2中发现了以下行为。

它也在 CPython 3.7.0 的 Linux 64 位下重现。

import inspect
def decorate(f):
lines = inspect.stack()[1].code_context
print(f.__name__, lines)
return f
@decorate
class Foo:
pass
@decorate
class Bar(dict):
pass

输出

Foo ['@decoraten']
Bar ['class Bar(dict):n']

为什么继承会改变inspect的行为?

进一步的实验表明,这是Python行号分配的一个怪癖。特别是,如果我们使用dis来查看带有和没有基类的代码反汇编:

import dis
import sys
dis.dis(sys._getframe().f_code)
def dec(): pass
@dec
class Foo: pass
@dec
class Bar(Foo): pass

我们看到,对于Foo,所涉及的指令有第 8 行(对应于@dec行(:

8          58 LOAD_NAME                4 (dec)
61 LOAD_BUILD_CLASS
62 LOAD_CONST               4 (<code object Foo at 0x2b2a65422810, file "./prog.py", line 8>)
65 LOAD_CONST               5 ('Foo')
68 MAKE_FUNCTION            0
71 LOAD_CONST               5 ('Foo')
74 CALL_FUNCTION            2 (2 positional, 0 keyword pair)
77 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
80 STORE_NAME               5 (Foo)

但是对于Bar,对于加载基类的LOAD_NAME,行号从 11 前进到 12:

11          83 LOAD_NAME                4 (dec)
86 LOAD_BUILD_CLASS
87 LOAD_CONST               6 (<code object Bar at 0x2b2a654a0f60, file "./prog.py", line 11>)
90 LOAD_CONST               7 ('Bar')
93 MAKE_FUNCTION            0
96 LOAD_CONST               7 ('Bar')
12          99 LOAD_NAME                5 (Foo)
102 CALL_FUNCTION            3 (3 positional, 0 keyword pair)
105 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
108 STORE_NAME               6 (Bar)

如果没有基类,则当修饰器运行时,父框架的f_lineno位于@行上。对于基类,父帧位于加载基类行上。

最新更新