为什么使用列表理解会影响堆栈跟踪?

问题描述

我在玩 inspect 模块,并注意到当我使用列表理解时堆栈有一个额外的层。

这是一个基本示例。我希望两个打印函数调用都提供相同的堆栈引用(第 19 行和第 20 行),但是 print(get_list_comprehension()) 在第 16 行返回了对 inside get_list_comprehension 的堆栈引用。似乎列表推导式有效地向堆栈添加一个框架。

与手动构建列表相比,为什么会出现这种情况?

from inspect import getframeinfo,stack


def get_traceback(depth=2):
    return getframeinfo(stack()[depth][0])


def get_list():
    tracebacks = []
    for i in range(1):
        tracebacks.append(get_traceback())
    return tracebacks[0]


def get_list_comprehension():
    return [get_traceback() for _ in range(1)][0]


print(get_list())
print(get_list_comprehension())


# Traceback(filename='stack.py',lineno=19,function='<module>',code_context=['print(get_list())\n'],index=0)
# Traceback(filename='stack.py',lineno=16,function='get_list_comprehension',code_context=['    return [get_traceback() for _ in range(1)][0]\n'],index=0)

解决方法

因为列表推导式是使用函数实现的。您可以使用反汇编程序看到这一点:

>>> def get_list():
...     tracebacks = []
...     for i in range(1):
...         tracebacks.append(get_traceback())
...     return tracebacks[0]
...
>>>
>>> def get_list_comprehension():
...     return [get_traceback() for _ in range(1)][0]
...
>>>

所以,没有列表理解:

>>> import dis
>>> dis.dis(get_list)
  2           0 BUILD_LIST               0
              2 STORE_FAST               0 (tracebacks)

  3           4 SETUP_LOOP              28 (to 34)
              6 LOAD_GLOBAL              0 (range)
              8 LOAD_CONST               1 (1)
             10 CALL_FUNCTION            1
             12 GET_ITER
        >>   14 FOR_ITER                16 (to 32)
             16 STORE_FAST               1 (i)

  4          18 LOAD_FAST                0 (tracebacks)
             20 LOAD_METHOD              1 (append)
             22 LOAD_GLOBAL              2 (get_traceback)
             24 CALL_FUNCTION            0
             26 CALL_METHOD              1
             28 POP_TOP
             30 JUMP_ABSOLUTE           14
        >>   32 POP_BLOCK

  5     >>   34 LOAD_FAST                0 (tracebacks)
             36 LOAD_CONST               2 (0)
             38 BINARY_SUBSCR
             40 RETURN_VALUE 

使用列表理解:

>>> dis.dis(get_list_comprehension)
  2           0 LOAD_CONST               1 (<code object <listcomp> at 0x7fdd8f30f930,file "<stdin>",line 2>)
              2 LOAD_CONST               2 ('get_list_comprehension.<locals>.<listcomp>')
              4 MAKE_FUNCTION            0
              6 LOAD_GLOBAL              0 (range)
              8 LOAD_CONST               3 (1)
             10 CALL_FUNCTION            1
             12 GET_ITER
             14 CALL_FUNCTION            1
             16 LOAD_CONST               4 (0)
             18 BINARY_SUBSCR
             20 RETURN_VALUE

Disassembly of <code object <listcomp> at 0x7fdd8f30f930,line 2>:
  2           0 BUILD_LIST               0
              2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                10 (to 16)
              6 STORE_FAST               1 (_)
              8 LOAD_GLOBAL              0 (get_traceback)
             10 CALL_FUNCTION            0
             12 LIST_APPEND              2
             14 JUMP_ABSOLUTE            4
        >>   16 RETURN_VALUE
,

嗯,因为确实如此。扩展列表理解是在内部函数中完成的。如果您将 get_traceback 的内容替换为 assert 0,您会看到:

C:\tmp>python x.py
Traceback (most recent call last):
  File "x.py",line 19,in <module>
    print(get_list())
  File "x.py",line 11,in get_list
    tracebacks.append(get_traceback())
  File "x.py",line 5,in get_traceback
    assert 0
AssertionError

C:\tmp>python x.py
Traceback (most recent call last):
  File "x.py",line 20,in <module>
    print(get_list_comprehension())
  File "x.py",line 16,in get_list_comprehension
    return [get_traceback() for _ in range(1)][0]
  File "x.py",in <listcomp>
    return [get_traceback() for _ in range(1)][0]
  File "x.py",in get_traceback
    assert 0
AssertionError

C:\tmp>

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...