为什么在for循环中允许使用列表订阅? [英] Why is using a list subscription allowed in a for loop?
问题描述
以下结构在Python中如何被接受:
How is the following construct accepted in Python:
l = [1, 2, 3, 4]
for i, l[i] in enumerate(l[:]):
print(l[i])
似乎没有任何抱怨,它很高兴地打印出1 2 3 4
.怎么允许的?它到底是做什么的?
There seem to be no complaints and it happily prints out 1 2 3 4
. How is this allowed and what exactly does it do?
推荐答案
The syntax rule for for
loops allows the iteration variables to be any of those specified in target_list
:
for_stmt ::= "for" target_list "in" expression_list ":" suite
["else" ":" suite]
其中 target_list
允许用于以下结构:
where target_list
allows for the following constructs:
target_list ::= target ("," target)* [","]
target ::= identifier
| "(" [target_list] ")"
| "[" [target_list] "]"
| attributeref
| subscription
| slicing
| "*" target
这意味着您还可以执行其他古怪的事情,例如分配给切片:
This means you can also do other wacky things like assign to slices:
for l[::-1] in [l, l, l]: pass
或订阅:
class Foo: a = 20
for Foo.a in range(2): pass
但是我真的不知道你为什么要这么做.
but I really have no idea why you'd want to do so.
这是for-loop
的副产品,基本上在每次迭代中都执行赋值语句,如参考文献中所述:
This is a by-product of for-loop
s essentially performing an assignment statement for every iteration, as stated in the reference:
依次使用标准分配规则将每个项目分配给目标列表 (请参见分配声明),然后执行套件.
Each item in turn is assigned to the target list using the standard rules for assignments (see Assignment statements), and then the suite is executed.
所以循环的作用是,它从expression_list
获取迭代器,并对target_list
中的每个值进行赋值.本质上等效于以下while
循环:
So what the loop does is, it gets the iterator from the expression_list
and performs an assignment to each of the values in the target_list
. Essentially equivalent to the following while
loop:
it = enumerate(l[:])
while True:
try:
i, l[i] = next(it)
print(l[i])
except StopIteration:
break
dis
也可以在字节码级别显示此行为.使用稍微简化的版本:
dis
can also show this behavior manifesting on the byte-code level. Using a slightly simplified version:
def _():
for i, l[i] in enumerate(l[:]):
pass
您将获得以下输出:
dis(_)
2 0 SETUP_LOOP 40 (to 43)
3 LOAD_GLOBAL 0 (enumerate)
6 LOAD_GLOBAL 1 (l)
9 LOAD_CONST 0 (None)
12 LOAD_CONST 0 (None)
15 BUILD_SLICE 2
18 BINARY_SUBSCR
19 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
22 GET_ITER
>> 23 FOR_ITER 16 (to 42)
26 UNPACK_SEQUENCE 2
29 STORE_FAST 0 (i)
32 LOAD_GLOBAL 1 (l)
35 LOAD_FAST 0 (i)
38 STORE_SUBSCR
3 39 JUMP_ABSOLUTE 23
>> 42 POP_BLOCK
>> 43 LOAD_CONST 0 (None)
46 RETURN_VALUE
在FOR_ITER
命令之后立即执行相关分配的地方:
where the relevant assignment is performed right after the the FOR_ITER
command:
26 UNPACK_SEQUENCE 2
29 STORE_FAST 0 (i)
32 LOAD_GLOBAL 1 (l)
35 LOAD_FAST 0 (i)
38 STORE_SUBSCR
解压缩序列并将其分配给i
和l[i]
.
unpacks the sequence and assigns it to i
and l[i]
.
如果还反汇编dis('i, l[i] = (1, 2)')
,则会看到如果忽略元组(1, 2)
的初始加载和值的返回,则操作完全相同.
If you also disassemble dis('i, l[i] = (1, 2)')
you'll see that if you ignore the initial loading of the tuple (1, 2)
and the returning of the value, the operations are exactly the same.
这篇关于为什么在for循环中允许使用列表订阅?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!