Python 3与Python 2二维列表初始化结果不一致问题排查
Python 2与Python 3列表推导式循环变量作用域差异问题
我尝试用列表推导式初始化二维列表时,发现Python 2和Python 3的执行结果存在差异,示例代码如下(导入语句用于在Python 2中使用Python 3的print语法):
from __future__ import print_function ROW = 3 COL = 3 myList = [0 for i in range(ROW)] #print('myList1 =', myList, 'len =', len(myList)) for i in range(len(myList)): #print('i =', i) myList[i] = [-1 for i in range(COL)] #myList[0] = [-1 for i in range(COL)] print('myList2 =', myList)
两个版本的输出分别为:
- Python 3 输出:
myList2 = [[-1, -1, -1], [-1, -1, -1], [-1, -1, -1]] - Python 2 输出:
myList2 = [0, 0, [-1, -1, -1]]
执行赋值操作前,两个版本的执行过程一致;但循环内的赋值仅最后一次生效,手动在循环外执行myList[0] = ...这类操作却能正常更新元素,请问忽略了什么要点?
原因分析
问题出在Python 2与Python 3对列表推导式循环变量的作用域处理不同:
- Python 3中,列表推导式的循环变量是局部变量,不会影响外层作用域的同名变量。外层循环的
i会依次取0、1、2,三次赋值操作都能正常执行。 - Python 2中,列表推导式的循环变量会泄露到外层作用域,和外层循环的
i共用同一个变量。当第一次执行外层循环(i=0)时,内部的列表推导式[-1 for i in range(COL)]会把i覆盖为2(因为range(COL)遍历到最后一个值是2),此时外层循环的i也变成了2。接下来外层循环的下一次迭代,i会变成3,超出range(len(myList))(即range(3))的范围,循环直接终止,因此只有myList[2]被赋值,前两个元素保留初始值0。
解决方法
修改列表推导式中的循环变量名,避免和外层循环的变量冲突,比如把内部的i改成j:
from __future__ import print_function ROW = 3 COL = 3 myList = [0 for i in range(ROW)] for i in range(len(myList)): myList[i] = [-1 for j in range(COL)] print('myList2 =', myList)
修改后,Python 2和Python 3的执行结果都会符合预期。
内容的提问来源于stack exchange,提问作者jml
相关产品推荐
相关产品推荐

