Loa*_*oax 21 python generator python-3.x pdb ipdb
在Python 3中运行此代码后:
import pdb
def foo():
nums = [1, 2, 3]
a = 5
pdb.set_trace()
foo()
Run Code Online (Sandbox Code Playgroud)
以下表达式有效:
(Pdb) print(nums)
[1, 2, 3]
(Pdb) print(a)
5
(Pdb) [x for x in nums]
[1, 2, 3]
Run Code Online (Sandbox Code Playgroud)
但是以下表达式失败:
(Pdb) [x*a for x in nums]
*** NameError: global name 'a' is not defined
Run Code Online (Sandbox Code Playgroud)
以上在Python 2.7中运行良好.
这是一个错误还是我遗失了什么?
更新:查看新接受的答案.这确实是一个错误(或有问题的设计),现在通过在pdb中引入新的命令和模式来解决.
Ben*_*man 17
如果您键入interact[i] pdb会话,您将获得一个交互式会话,并且列表推导在此模式下按预期工作
来源:http://bugs.python.org/msg215963
它工作得非常好:
>>> import pdb
>>> def f(seq):
... pdb.set_trace()
...
>>> f([1,2,3])
--Return--
> <stdin>(2)f()->None
(Pdb) [x for x in seq]
[1, 2, 3]
(Pdb) [x in seq for x in seq]
[True, True, True]
Run Code Online (Sandbox Code Playgroud)
没有显示你实际在做什么,没有人可以告诉你为什么在你的具体情况下你有一个NameError.
TL; DR在python3中,list-comprehensions实际上是具有自己的堆栈帧的函数,并且你不能从内部堆栈帧访问seq变量,这是一个参数test.它被视为全局(因此,未找到).
你看到的是python2与python3中list-comprehension的不同实现.在python 2中,list-comprehensions实际上是for循环的简写,你可以在字节码中清楚地看到它:
>>> def test(): [x in seq for x in seq]
...
>>> dis.dis(test)
1 0 BUILD_LIST 0
3 LOAD_GLOBAL 0 (seq)
6 GET_ITER
>> 7 FOR_ITER 18 (to 28)
10 STORE_FAST 0 (x)
13 LOAD_FAST 0 (x)
16 LOAD_GLOBAL 0 (seq)
19 COMPARE_OP 6 (in)
22 LIST_APPEND 2
25 JUMP_ABSOLUTE 7
>> 28 POP_TOP
29 LOAD_CONST 0 (None)
32 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
注意字节码如何包含FOR_ITER循环.另一方面,在python3中,list-comprehension实际上是具有自己的堆栈框架的函数:
>>> def test(): [x in seq2 for x in seq]
...
>>> dis.dis(test)
1 0 LOAD_CONST 1 (<code object <listcomp> at 0xb6fef160, file "<stdin>", line 1>)
3 MAKE_FUNCTION 0
6 LOAD_GLOBAL 0 (seq)
9 GET_ITER
10 CALL_FUNCTION 1
13 POP_TOP
14 LOAD_CONST 0 (None)
17 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
正如你可以看到有没有FOR_ITER在这里,而不是有一个MAKE_FUNCTION与CALL_FUNCTION字节码.如果我们检查列表理解的代码,我们可以理解如何设置绑定:
>>> test.__code__.co_consts[1]
<code object <listcomp> at 0xb6fef160, file "<stdin>", line 1>
>>> test.__code__.co_consts[1].co_argcount # it has one argument
1
>>> test.__code__.co_consts[1].co_names # global variables
('seq2',)
>>> test.__code__.co_consts[1].co_varnames # local variables
('.0', 'x')
Run Code Online (Sandbox Code Playgroud)
这.0是函数的唯一参数.x是循环的局部变量,seq2是一个全局变量.请注意.0,list-comprehension参数是从中获取的迭代seq,而不是seq自身.(参见上面GET_ITER输出中的操作码dis).一个更复杂的例子更清楚:
>>> def test():
... [x in seq for x in zip(seq, a)]
...
>>> dis.dis(test)
2 0 LOAD_CONST 1 (<code object <listcomp> at 0xb7196f70, file "<stdin>", line 2>)
3 MAKE_FUNCTION 0
6 LOAD_GLOBAL 0 (zip)
9 LOAD_GLOBAL 1 (seq)
12 LOAD_GLOBAL 2 (a)
15 CALL_FUNCTION 2
18 GET_ITER
19 CALL_FUNCTION 1
22 POP_TOP
23 LOAD_CONST 0 (None)
26 RETURN_VALUE
>>> test.__code__.co_consts[1].co_varnames
('.0', 'x')
Run Code Online (Sandbox Code Playgroud)
在这里你可以看到list-comprehension的唯一参数,总是表示为.0,是从中获得的iterable zip(seq, a).seq并且a他们自己没有被传递到列表理解.只iter(zip(seq, a))在list-comprehension中传递.
我们必须做的另一个观察是,当您运行时pdb,您无法从要定义的函数访问当前函数的上下文.例如,以下代码在python2和python3上均失败:
>>> import pdb
>>> def test(seq): pdb.set_trace()
...
>>> test([1,2,3])
--Return--
> <stdin>(1)test()->None
(Pdb) def test2(): print(seq)
(Pdb) test2()
*** NameError: global name 'seq' is not defined
Run Code Online (Sandbox Code Playgroud)
它失败了,因为在定义变量时将test2其seq视为全局变量,但它实际上是test函数内部的局部变量,因此无法访问.
您看到的行为类似于以下场景:
#python 2 no error
>>> class A(object):
... x = 1
... L = [x for _ in range(3)]
...
>>>
#python3 error!
>>> class A(object):
... x = 1
... L = [x for _ in range(3)]
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in A
File "<stdin>", line 3, in <listcomp>
NameError: global name 'x' is not defined
Run Code Online (Sandbox Code Playgroud)
第一个没有给出错误,因为它大部分相当于:
>>> class A(object):
... x = 1
... L = []
... for _ in range(3): L.append(x)
...
Run Code Online (Sandbox Code Playgroud)
由于列表推导在字节码中被"扩展".在python3中,它失败了,因为您实际上是在定义一个函数,并且您无法从嵌套的函数作用域访问类作用域:
>>> class A(object):
... x = 1
... def test():
... print(x)
... test()
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in A
File "<stdin>", line 4, in test
NameError: global name 'x' is not defined
Run Code Online (Sandbox Code Playgroud)
请注意,genexp在python2上实现为函数,实际上你会看到与它们类似的行为(在python2和python3上):
>>> import pdb
>>> def test(seq): pdb.set_trace()
...
>>> test([1,2,3])
--Return--
> <stdin>(1)test()->None
(Pdb) list(x in seq for x in seq)
*** Error in argument: '(x in seq for x in seq)'
Run Code Online (Sandbox Code Playgroud)
这里pdb没有给出更多细节,但失败的原因完全相同.
总结:它不是一个bug,pdb而是python实现范围的方式.更改此项以允许您尝试执行此操作的AFAIK pdb需要对函数的处理方式进行一些重大更改,并且我不知道是否可以在不修改解释器的情况下完成此操作.
请注意,在使用嵌套列表推导时,嵌套循环在字节码中扩展,如python2中的list-comprehensions:
>>> import dis
>>> def test(): [x + y for x in seq1 for y in seq2]
...
>>> dis.dis(test)
1 0 LOAD_CONST 1 (<code object <listcomp> at 0xb71bf5c0, file "<stdin>", line 1>)
3 MAKE_FUNCTION 0
6 LOAD_GLOBAL 0 (seq1)
9 GET_ITER
10 CALL_FUNCTION 1
13 POP_TOP
14 LOAD_CONST 0 (None)
17 RETURN_VALUE
>>> # The only argument to the listcomp is seq1
>>> import types
>>> func = types.FunctionType(test.__code__.co_consts[1], globals())
>>> dis.dis(func)
1 0 BUILD_LIST 0
3 LOAD_FAST 0 (.0)
>> 6 FOR_ITER 29 (to 38)
9 STORE_FAST 1 (x)
12 LOAD_GLOBAL 0 (seq2)
15 GET_ITER
>> 16 FOR_ITER 16 (to 35)
19 STORE_FAST 2 (y)
22 LOAD_FAST 1 (x)
25 LOAD_FAST 2 (y)
28 BINARY_ADD
29 LIST_APPEND 3
32 JUMP_ABSOLUTE 16
>> 35 JUMP_ABSOLUTE 6
>> 38 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
如您所见,字节码listcomp有明确的FOR_ITER结束seq2.这个显式FOR_ITER位于listcomp函数内部,因此对范围的限制仍然适用(例如seq2,作为全局加载).
事实上,我们可以使用pdb以下方法确认:
>>> import pdb
>>> def test(seq1, seq2): pdb.set_trace()
...
>>> test([1,2,3], [4,5,6])
--Return--
> <stdin>(1)test()->None
(Pdb) [x + y for x in seq1 for y in seq2]
*** NameError: global name 'seq2' is not defined
(Pdb) [x + y for x in non_existent for y in seq2]
*** NameError: name 'non_existent' is not defined
Run Code Online (Sandbox Code Playgroud)
注意NameError是seq2和不是seq1(它作为函数参数传递),并注意如何将第一个可迭代名称更改为不存在的名称会更改NameError(这意味着在第一种情况下seq1成功传递).