这更像是一个概念性问题 . 我最近在Python中看到了一段代码(它在2.7中运行,它也可能在2.5中运行),其中 for
循环对正在迭代的列表和该项中的项使用相同的名称 . 列表,这既是糟糕的做法,也是一些根本不起作用的东西 .
例如:
x = [1,2,3,4,5]
for x in x:
print x
print x
产量:
1
2
3
4
5
5
现在,对我来说,打印的最后一个值是从循环中分配给x的最后一个值是有道理的,但是我不明白为什么你能够为 for
循环的两个部分使用相同的变量名 . 让它按预期运作 . 它们在不同的范围内吗?引擎盖下发生了什么让这样的事情发挥作用?
6 回答
dis
告诉我们什么:关键位是第2和第3部分 - 我们从
x
(24 LOAD_NAME 0 (x)
)中加载该值,然后我们得到它的迭代器(27 GET_ITER
)并开始迭代它(28 FOR_ITER
) . Python永远不会再回来加载迭代器了 .旁白:这样做没有任何意义,因为它已经有了迭代器,并且Abhijit points out in his answer,Section 7.3 of Python's specification实际上需要这种行为) .
当名称_562504被覆盖以指向以前称为_562505的列表中的每个值时,Python在查找迭代器时没有任何问题,因为它永远不需要再次查看名称
x
来完成迭代协议 .使用示例代码作为核心参考
我希望您参考手册中的7.3. The for statement部分
Excerpt 1
这意味着您的变量
x
,它是对象list
:[1,2,3,4,5]
的符号名称,被计算为可迭代对象 . 即使变量,符号引用改变其效忠,因为expression-list不再被评估,对已经评估和生成的可迭代对象没有影响 .Note
Python中的所有内容都是一个Object,具有标识符,属性和方法 .
变量是符号名称,是对任何给定实例的一个且仅一个对象的引用 .
运行时的变量可以改变其忠诚度,即可以引用其他一些对象 .
Excerpt 2
这里套件引用迭代器而不是表达式列表 . 因此,对于每次迭代,执行迭代器以产生下一个项而不是引用原始表达式列表 .
如果你考虑一下,它必须以这种方式工作 .
for
循环序列的表达式可以是任何东西:我们可以't query the sequence on each pass through the loop, or here we'd第二次从下一批5字节开始读取 . 当然,Python必须以某种方式在循环开始之前私下存储表达式的结果 .
不 . 要确认这一点,您可以保留对原始范围字典(locals())的引用,并注意到您实际上在循环中使用相同的变量:
Sean Vieira显示了幕后发生了什么,但是为了用更易读的python代码来描述它,你的
for
循环基本上等同于这个while
循环:这与您在旧版Java中看到的传统索引迭代方法不同,例如:
当item变量和sequence变量相同时,此方法将失败,因为在第一次
x
被重新分配给第一个项目之后,序列x
将不再可用于查找下一个索引 .但是,使用前一种方法,第一行(
it = iter(x)
)请求iterator object,这实际上是负责从那时开始提供下一个项目 . 不再需要直接访问x
最初指向的序列 .x
不再引用原始x
列表,因此's no confusion. Basically, python remembers it'迭代原始x
列表,但只要您开始将迭代值(0,1,2等)分配给名称x
,它就不再引用原来的x
清单 . 该名称将重新分配给迭代值 .它是变量(x)和它指向的对象(列表)之间的差异 . 当for循环开始时,Python会抓取对x指向的对象的内部引用 . 它使用对象而不是x在任何给定时间引用的内容 .
如果重新分配x,则for循环不会更改 . 如果x指向可变对象(例如,列表)并且您更改该对象(例如,删除元素),则结果可以是不可预知的 .
基本上,for循环接受列表
x
,然后将其存储为临时变量, re 为该临时变量中的每个值分配x
. 因此,x
现在是列表中的最后一个值 .就像这样:
在此示例中,
x
作为bar
存储在foo()
中,因此虽然x
正在重新分配,但它仍然存在于foo()
中,因此我们可以使用它来触发for
循环 .