Why can I use the same name for iterator and sequence in a Python for loop?
这更像是一个概念性问题。我最近在python中看到了一段代码(它在2.7中工作,也可能在2.5中运行),其中一个
例如:
1 2 3 4 | x = [1,2,3,4,5] for x in x: print x print x |
产量:
1 2 3 4 5 6 | 1 2 3 4 5 5 |
现在,对我来说,最后一个打印的值是从循环中分配给x的最后一个值是有意义的,但是我不明白为什么您可以对
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | Python 3.4.1 (default, May 19 2014, 13:10:29) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin Type"help","copyright","credits" or"license" for more information. >>> from dis import dis >>> dis("""x = [1,2,3,4,5] ... for x in x: ... print(x) ... print(x)""") 1 0 LOAD_CONST 0 (1) 3 LOAD_CONST 1 (2) 6 LOAD_CONST 2 (3) 9 LOAD_CONST 3 (4) 12 LOAD_CONST 4 (5) 15 BUILD_LIST 5 18 STORE_NAME 0 (x) 2 21 SETUP_LOOP 24 (to 48) 24 LOAD_NAME 0 (x) 27 GET_ITER >> 28 FOR_ITER 16 (to 47) 31 STORE_NAME 0 (x) 3 34 LOAD_NAME 1 (print) 37 LOAD_NAME 0 (x) 40 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 43 POP_TOP 44 JUMP_ABSOLUTE 28 >> 47 POP_BLOCK 4 >> 48 LOAD_NAME 1 (print) 51 LOAD_NAME 0 (x) 54 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 57 POP_TOP 58 LOAD_CONST 5 (None) 61 RETURN_VALUE |
关键位是第2节和第3节-我们从
旁白:这样做没有任何意义,因为它已经有了迭代器,正如Abhijit在回答中指出的那样,Python规范的第7.3节实际上需要这种行为)。
当名称
使用示例代码作为核心参考
1 2 3 4 | x = [1,2,3,4,5] for x in x: print x print x |
我想请你参考第7.3节。手册中的for语句
节选1
The expression list is evaluated once; it should yield an iterable
object. An iterator is created for the result of the expression_list.
这意味着变量
注释
- python中的一切都是一个对象,有一个标识符、属性和方法。
- 变量是符号名,是对任何给定实例中一个且只有一个对象的引用。
- 运行时的变量可以改变它的效度,即可以引用其他对象。
节选2
The suite is then executed once for each item provided by the
iterator, in the order of ascending indices.
在这里,这个套件引用的是迭代器,而不是表达式列表。因此,对于每次迭代,都会执行迭代器来生成下一个项,而不是引用原始表达式列表。
如果你考虑的话,它必须这样工作。
1 2 3 | binaryfile = open("file","rb") for byte in binaryfile.read(5): ... |
我们不能在每次通过循环时查询序列,或者在这里,我们将在第二次读取下一批5字节的数据。当然,在循环开始之前,Python必须以某种方式私下存储表达式的结果。
Are they in different scopes?
否。要确认这一点,您可以保留对原始作用域字典(locals())的引用,并注意您实际上在循环中使用的是相同的变量:
1 2 3 4 5 6 | x = [1,2,3,4,5] loc = locals() for x in x: print locals() is loc # True print loc["x"] # 1 break |
What's going on under the hood that allows something like this to
work?
Sean Vieira准确地展示了引擎盖下发生的事情,但是为了用更可读的python代码来描述它,您的
1 2 3 4 5 6 7 | it = iter(x) while True: try: x = it.next() except StopIteration: break print x |
这与传统的索引方法不同,例如在旧版本的Java中可以看到:
1 2 3 4 | for (int index = 0; index < x.length; index++) { x = x[index]; ... } |
当item变量和sequence变量相同时,此方法将失败,因为在第一次将
然而,对于前一种方法,第一行(
它是变量(x)和它指向的对象(列表)之间的区别。当for循环开始时,python获取一个指向x所指向的对象的内部引用。它使用对象,而不是x在任何给定时间引用的对象。
如果重新分配X,则for循环不会更改。如果X指向可变对象(例如列表),而您更改了该对象(例如删除元素),则结果可能不可预测。
基本上,for循环接受列表
1 2 3 4 5 6 | >>> x = [1, 2, 3] >>> [x for x in x] [1, 2, 3] >>> x 3 >>> |
就像这样:
1 2 3 4 5 6 7 8 9 10 11 | >>> def foo(bar): ... return bar ... >>> x = [1, 2, 3] >>> for x in foo(x): ... print x ... 1 2 3 >>> |
在本例中,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | In [1]: x = range(5) In [2]: x Out[2]: [0, 1, 2, 3, 4] In [3]: id(x) Out[3]: 4371091680 In [4]: for x in x: ...: print id(x), x ...: 140470424504688 0 140470424504664 1 140470424504640 2 140470424504616 3 140470424504592 4 In [5]: id(x) Out[5]: 140470424504592 |