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

Oli*_*çon 6 python decorator inspect python-decorators python-3.6

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

在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
Run Code Online (Sandbox Code Playgroud)

产量

Foo ['@decorate\n']
Bar ['class Bar(dict):\n']
Run Code Online (Sandbox Code Playgroud)

为什么继承改变了行为inspect

use*_*ica 6

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

import dis
import sys

dis.dis(sys._getframe().f_code)

def dec(): pass

@dec
class Foo: pass

@dec
class Bar(Foo): pass
Run Code Online (Sandbox Code Playgroud)

我们看到,因为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)
Run Code Online (Sandbox Code Playgroud)

但是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)
Run Code Online (Sandbox Code Playgroud)

没有基类,当装饰器运行时,父框架f_lineno@在行上.对于基类,父框架位于load-the-base-class行上.