为什么检查 return 不同的行 class 从 superclass 继承?
Why does inspect return different line for class inheriting from superclass?
在尝试弄清楚是否 时,我们意识到 inspect
在查看继承自 superclass 的修饰 class 时具有不同的行为。
在 Windows 10 下的 CPython 3.6.2 中发现了以下行为。
它也在 Linux 64 位下的 CPython 3.7.0 中被复制。
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 ['@decorate\n']
Bar ['class Bar(dict):\n']
为什么继承会改变 inspect
的行为?
进一步的实验表明,这是 Python 的行号分配的一个怪癖。特别是,如果我们使用 dis
来查看 code with and without a base class 的反汇编:
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
,对于加载基础class:
的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)
没有基础 class,装饰器运行时父框架的 f_lineno
在 @
行。对于基础 class,父框架位于加载基础 -class 行。
在尝试弄清楚是否 inspect
在查看继承自 superclass 的修饰 class 时具有不同的行为。
在 Windows 10 下的 CPython 3.6.2 中发现了以下行为。
它也在 Linux 64 位下的 CPython 3.7.0 中被复制。
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 ['@decorate\n']
Bar ['class Bar(dict):\n']
为什么继承会改变 inspect
的行为?
进一步的实验表明,这是 Python 的行号分配的一个怪癖。特别是,如果我们使用 dis
来查看 code with and without a base class 的反汇编:
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
,对于加载基础class:
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)
没有基础 class,装饰器运行时父框架的 f_lineno
在 @
行。对于基础 class,父框架位于加载基础 -class 行。