代码之家  ›  专栏  ›  技术社区  ›  101

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

  •  1
  • 101  · 技术社区  · 3 年前

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

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

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

    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)
    
    2 回复  |  直到 3 年前
        1
  •  1
  •   Tim Roberts    3 年前

    好吧,因为确实如此。扩展列表理解是在一个内部函数中完成的。如果你替换了 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>
    
        2
  •  2
  •   juanpa.arrivillaga    3 年前

    因为列表理解是使用函数实现的。您可以使用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