使用列表生成器时,Python3中的pdb模块中可能存在错误
在Python 3中运行此代码后:使用列表生成器时,Python3中的pdb模块中可能存在错误,python,python-3.x,generator,pdb,ipdb,Python,Python 3.x,Generator,Pdb,Ipdb,在Python 3中运行此代码后: import pdb def foo(): nums = [1, 2, 3] a = 5 pdb.set_trace() foo() 以下表达式有效: (Pdb) print(nums) [1, 2, 3] (Pdb) print(a) 5 (Pdb) [x for x in nums] [1, 2, 3] 但以下表达式失败: (Pdb) [x*a for x in nums] *** NameError: global n
import pdb
def foo():
nums = [1, 2, 3]
a = 5
pdb.set_trace()
foo()
以下表达式有效:
(Pdb) print(nums)
[1, 2, 3]
(Pdb) print(a)
5
(Pdb) [x for x in nums]
[1, 2, 3]
但以下表达式失败:
(Pdb) [x*a for x in nums]
*** NameError: global name 'a' is not defined
上述内容在Python2.7中运行良好
这是一个错误还是我遗漏了什么
更新:查看新的已接受答案。这确实是一个bug(或一个有问题的设计),现在已经通过在pdb中引入一个新的命令和模式来解决了。我不明白,如果您希望为seq中的每个元素生成一个Trues列表,那么为什么不[True for x in seq]-我想在尝试这类操作之前,您需要先分配一个本地副本。它工作得非常好:
>>> 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]
注意字节码如何包含一个FOR ITER的循环。另一方面,在python3中,列表理解实际上是具有自己堆栈框架的函数:
>>> 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
这里.0
是函数的唯一参数x
是循环的局部变量,seq2
是全局变量。请注意,列表理解参数.0
,是从seq
获取的iterable,而不是seq
本身。(参见上文dis
输出中的GET_ITER
操作码)。通过一个更复杂的示例,这一点更为清楚:
>>> 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')
它失败是因为定义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
因为列表理解在字节码中被“扩展”。在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
如您所见,listcomp
的字节码在seq2
上有一个显式的for ITER
。
此显式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
导入pdb
>>>def测试(seq1,seq2):pdb.set_trace()
...
>>>测试([1,2,3],[4,5,6])
--返回--
>(1)测试()->无
(Pdb)[x+y代表序列1中的x代表序列2中的y]
***NameError:未定义全局名称“seq2”
(Pdb)[x+y表示序列2中的x不存在表示序列2中的y]
***NameError:未定义名称“不存在”
注意
namererror
是关于seq2
而不是seq1
(作为函数参数传递),并注意将第一个可引用名称更改为不存在的名称如何更改namererror
(这意味着在第一种情况下seq1
成功传递).如果您在[i]pdb会话中键入interact
,您将获得一个交互式会话,并且列表理解在此模式下按预期工作
来源:奇怪,这对我来说确实适用于
ipdb==0.7
和ipython==0.13.2
它在IPython3 0.12.1和Python 3.2.3中失败了。弹出这个提示:奇怪地尝试一下,它在Python 2.7.3中也确实有效(默认,2012年8月1日,05:14:39)[GCC 4.6.3],但在Python 3.2.3中失败(默认,2012年10月19日,20:10:41)[GCC 4.6.3]。希望这足够详细。我将把这些细节添加到问题中。@Loax更新了我的答案。这种不同的行为是由于在python3中如何实现列表理解的范围问题造成的。@Baruriu感谢您的回答。这无疑为这个问题提供了很多线索。我编辑了这个问题,并添加了我认为更好的例子。阅读您的答案后,有两个后续问题:1)在更新问题中给出的示例中,“nums”和“a”都是局部变量。在pdb的列表理解中,似乎只有出现在“for”之前的局部变量是有问题的。这是否意味着'nums'作为参数传递给列表理解函数,但'a'被认为是全局的?2)Python支持闭包,pdb是否可能传递对当前stackframe的引用(假设它实际上不在堆栈中)这样定义的函数就可以从该框架中查找非局部变量了?遗憾的是,这个“功能”使得pdb在Python 3中几乎不可用。我试图理解为什么一个看起来非常好的表达式在pdb中失败。给定的代码示例除了帮助理解正在发生的事情之外没有任何其他用途。如何退出交互模式?
#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
>>> class A(object):
... x = 1
... L = []
... for _ in range(3): L.append(x)
...
>>> 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
>>> 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)'
>>> 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
>>> 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