为什么使用列表理解会影响堆栈跟踪?
Why does using list comprehension affect the stack trace?
我在玩 inspect
模块,并注意到当我使用列表理解时堆栈有一个额外的层。
这是一个基本示例。我希望两个打印函数调用都提供相同的堆栈引用(第 19 行和第 20 行),但是 print(get_list_comprehension())
returns 对 inside [=13= 的堆栈引用] 在第 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)
嗯,因为确实如此。扩展列表理解是在内部函数中完成的。如果将 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>
因为列表推导是使用函数实现的。您可以使用反汇编器看到这一点:
>>> 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
我在玩 inspect
模块,并注意到当我使用列表理解时堆栈有一个额外的层。
这是一个基本示例。我希望两个打印函数调用都提供相同的堆栈引用(第 19 行和第 20 行),但是 print(get_list_comprehension())
returns 对 inside [=13= 的堆栈引用] 在第 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)
嗯,因为确实如此。扩展列表理解是在内部函数中完成的。如果将 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>
因为列表推导是使用函数实现的。您可以使用反汇编器看到这一点:
>>> 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