【问题标题】:Why does using list comprehension affect the stack trace?为什么使用列表推导会影响堆栈跟踪?
【发布时间】:2021-04-08 23:52:02
【问题描述】:

我在玩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)

【问题讨论】:

  • 因为列表推导是通过函数实现的。

标签: python list-comprehension stack-trace


【解决方案1】:

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

>>> 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, file "<stdin>", 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

【讨论】:

    【解决方案2】:

    嗯,因为确实如此。扩展列表理解是在内部函数中完成的。如果您将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", line 16, in <listcomp>
        return [get_traceback() for _ in range(1)][0]
      File "x.py", line 5, in get_traceback
        assert 0
    AssertionError
    
    C:\tmp>
    

    【讨论】:

    • 那么这很有意义!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-20
    • 1970-01-01
    • 2020-08-14
    • 1970-01-01
    • 1970-01-01
    • 2019-08-05
    相关资源
    最近更新 更多