我不小心写了一些这样的代码:
foo = [42]
k = {'c': 'd'}
for k['z'] in foo: # Huh??
print k
但令我惊讶的是,这不是语法错误。相反,它会打印 {'c': 'd', 'z': 42}
。
我的猜测是该代码按字面意思翻译为:
i = iter(foo)
while True:
try:
k['z'] = i.next() # literally translated to assignment; modifies k!
print k
except StopIteration:
break
但是...为什么语言允许这样做?我希望 for-stmt's target expression 中只允许单个标识符和标识符元组。 .在任何情况下这实际上是有用的,而不仅仅是一个奇怪的问题?
最佳答案
for
循环遵循标准的赋值规则,因此在普通赋值的 LHS 上工作的内容应该与 for
一起工作:
Each item in turn is assigned to the target list using the standard rules for assignments
for
构造只是调用分配给目标的底层机制,在您的示例代码中是 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
显然,任何在常规作业中有效的方法,例如:
完整切片分配:
>>> for [][:] in []:
... pass
...
>>>
列表订阅
>>> for [2][0] in [42]:
... pass
...
>>>
字典订阅等将是有效的候选目标,唯一的异常(exception)是链式分配;不过,我暗中认为可以编写一些肮脏的语法来执行链接。
I would expect only single identifiers and tuples of identifiers
我想不出一个将字典键作为目标的好的用例。此外,在循环体中进行字典键分配比在 for
子句中使用它作为目标更具可读性。
然而,在常规赋值中非常有用的扩展解包(Python 3)在 for 循环中也同样方便:
>>> lst = [[1, '', '', 3], [3, '', '', 6]]
>>> for x, *y, z in lst:
... print(x,y,z)
...
1 ['', ''] 3
3 ['', ''] 6
这里也召唤了相应的分配给不同目标的机制;多个STORE_NAME
:
>>> 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
说明 for
只是简单的连续执行的赋值语句。
关于python - 为什么 for 循环中允许任意目标表达式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44317993/