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

2024-05-16 03:10:08 发布

您现在位置:Python中文网/ 问答频道 /正文

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

这里有一个基本的例子。我希望两个print函数调用都提供相同的堆栈引用(到第19行和第20行),但是print(get_list_comprehension())返回到第16行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)

Tags: in列表forgetreturnstack堆栈def
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>

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

>>> 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

相关问题 更多 >