为什么在for循环中允许任意的目标表达式? [英] Why are arbitrary target expressions allowed in for-loops?
问题描述
我不小心写了这样的代码:
foo = [42]
k = {'c': 'd'}
for f ['z'] in foo:#呃?
print k
但令我吃惊的是,这不是一个语法错误。相反,它会打印 {'c':'d','z':42}
。
= iter(foo)
而真:
尝试:
k ['z'] = i.next()#直接转换为赋值;修改k!
打印k
除了StopIteration:
break
为什么这是由语言所允许的?我希望在中只允许使用单个标识符和元组标识符目标表达式。有没有什么情况下,这实际上是有用的,不只是一个怪异的陷阱? 循环遵循标准的赋值规则,所以对于一个vanilla赋值的LHS可以在中使用:
每个项目依次使用标准的
规则分配给目标列表
对于结构只是简单地召唤分配给目标的底层机制,在你的示例代码中是
STORE_SUBSCR
:
>>> foo = [42]
>>> k = {'c':'d'}
>>> $ f $
$ GET_ITER
(' >> 6 FOR_ITER 8(至16)
8 LOAD_NAME 1(k)
10 LOAD_CONST 0('e')
12 STORE_SUBSCR< ------------- -------
14 JUMP_ABSOLUTE 6
>> 16 POP_BLOCK
>> 18 LOAD_CONST 1(无)
20 RETURN_VALUE
但令我惊讶的是,这不是一个语法错误
显然,以下内容:
完整分片分配:
>>> for [] [:] in []:
... pass
...
>>>
列表订阅
>>>对于[42]中的[2] [0]:
...通过
...
>>>
字典订阅等将是有效的候选目标,唯一的例外是链接指派强>;尽管我偷偷想到可以用一些脏语法来进行链接。
$ hr
$ b
我只希望只有单个标识符和元组标识符
我想不出一个好的用例为字典键作为目标。此外,在循环体中进行字典键赋值比在中使用子句中的目标更具可读性。
然而,在定期任务中非常有用的扩展解包(Python 3)在for循环中也同样适用:
$ b $ pre $ $ code>>>> lst = [[1,'','',3],[3,'','',6]]
>>>对于x,* y,z in lst:
... print(x,y,z)
...
1 ['',''] 3
3 ['',''] 6
此处也会调用相应的分配给不同目标的机制; STORE_NAME
s:
>>> disdis('for x,* y,z in lst:pass')
1 0 SETUP_LOOP 20(to 22)
2 LOAD_NAME 0(lst)
4 GET_ITER
>> 6 FOR_ITER 12(至20)
8 EXTENDED_ARG 1
10 UNPACK_EX 257
12 STORE_NAME 1(x)< -----
14 STORE_NAME 2(y)< -----
16 STORE_NAME 3(z)< -----
18 JUMP_ABSOLUTE 6
>> 20 POP_BLOCK
>> 22 LOAD_CONST 0(无)
24 RETURN_VALUE
表明一个 for
几乎不是简单的赋值语句,而是连续执行。
I accidentally wrote some code like this:
foo = [42]
k = {'c': 'd'}
for k['z'] in foo: # Huh??
print k
But to my surprise, this was not a syntax error. Instead, it prints {'c': 'd', 'z': 42}
.
My guess is that the code is translated literally to something like:
i = iter(foo)
while True:
try:
k['z'] = i.next() # literally translated to assignment; modifies k!
print k
except StopIteration:
break
But... why is this allowed by the language? I would expect only single identifiers and tuples of identifiers should be allowed in the for-stmt's target expression. Is there any situation in which this is actually useful, not just a weird gotcha?
The for
loop follows the standard rules of assignment so what works on the LHS of a vanilla assignment should work with the for
:
Each item in turn is assigned to the target list using the standard rules for assignments
The for
construct simply summons the underlying mechanism for assigning to the target which in the case of your sample code is STORE_SUBSCR
:
>>> foo = [42]
>>> k = {'c': 'd'}
>>> dis.dis('for k["e"] in foo: pass')
1 0 SETUP_LOOP 16 (to 18)
2 LOAD_NAME 0 (foo)
4 GET_ITER
>> 6 FOR_ITER 8 (to 16)
8 LOAD_NAME 1 (k)
10 LOAD_CONST 0 ('e')
12 STORE_SUBSCR <--------------------
14 JUMP_ABSOLUTE 6
>> 16 POP_BLOCK
>> 18 LOAD_CONST 1 (None)
20 RETURN_VALUE
But to my surprise, this was not a syntax error
Apparently, whatever works in a regular assignment such as the following:
full slice assignment:
>>> for [][:] in []:
... pass
...
>>>
list subscription
>>> for [2][0] in [42]:
... pass
...
>>>
dictionary subscription etc. would be valid candidate targets, with the lone exception being a chained assignment; although, I secretly think one can cook up some dirty syntax to perform the chaining.
I would expect only single identifiers and tuples of identifiers
I can't think of a good use case for a dictionary key as a target. Besides, it is more readable to do the dictionary key assignment in the loop body, than use it as a target in the for
clause.
However, extended unpacking (Python 3) which is very useful in regular assignments also comes equally handy in a for loop:
>>> lst = [[1, '', '', 3], [3, '', '', 6]]
>>> for x, *y, z in lst:
... print(x,y,z)
...
1 ['', ''] 3
3 ['', ''] 6
The corresponding mechanism for assigning to the different targets here is also summoned; multiple STORE_NAME
s:
>>> dis.dis('for x, *y, z in lst: pass')
1 0 SETUP_LOOP 20 (to 22)
2 LOAD_NAME 0 (lst)
4 GET_ITER
>> 6 FOR_ITER 12 (to 20)
8 EXTENDED_ARG 1
10 UNPACK_EX 257
12 STORE_NAME 1 (x) <-----
14 STORE_NAME 2 (y) <-----
16 STORE_NAME 3 (z) <-----
18 JUMP_ABSOLUTE 6
>> 20 POP_BLOCK
>> 22 LOAD_CONST 0 (None)
24 RETURN_VALUE
Goes to show that a for
is barely simple assignment statements executed successively.
这篇关于为什么在for循环中允许任意的目标表达式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!