我不小心写了这样的代码:
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中只允许使用单个标识符和标识符元组。是否存在实际有用的情况,而不仅仅是一个奇怪的问题?
答案 0 :(得分:32)
for
循环遵循标准的赋值规则,因此对于vanilla赋值的LHS起作用应该适用于for
:
依次使用标准将每个项目分配到目标列表 作业规则
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
但令我惊讶的是,这不是语法错误
显然,无论是在常规任务中如何工作,如下所示:
完整切片分配:
>>> for [][:] in []:
... pass
...
>>>
列出订阅
>>> for [2][0] in [42]:
... pass
...
>>>
字典订阅等将是有效的候选目标,唯一的例外是链式分配;虽然,我暗自认为可以用一些脏的语法来执行链接。
我希望只有单个标识符和标识符元组
我想不出字典键作为目标的好用例。此外,在循环体中进行字典键赋值比在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
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
表明for
几乎不是简单的赋值语句连续执行。
答案 1 :(得分:26)
以下代码有意义,对吧?
foo = [42]
for x in foo:
print x
for
循环将迭代列表foo
,并依次将每个对象分配给当前命名空间中的名称x
。结果将是单个迭代和42
的单个打印。
代替代码中的x
,您有k['z']
。 k['z']
是有效的存储名称。就像我的例子中的x
一样,它还不存在。实际上,它在全局命名空间中是k.z
。循环创建k.z
或k['z']
并将其在foo
中找到的值分配给它,就像创建x
一样,并在我的示例中为其分配值。如果你在foo中有更多的价值......
foo = [42, 51, "bill", "ted"]
k = {'c': 'd'}
for k['z'] in foo:
print k
会导致:
{'c': 'd', 'z': 42}
{'c': 'd', 'z': 51}
{'c': 'd', 'z': 'bill'}
{'c': 'd', 'z': 'ted'}
你写了完全有效的意外代码。它甚至不是奇怪的代码。您通常不会将字典条目视为变量。
即使代码不奇怪,如何允许这样的分配有用呢?
key_list = ['home', 'car', 'bike', 'locker']
loc_list = ['under couch', 'on counter', 'in garage', 'in locker']
chain = {}
for index, chain[key_list[index]] in enumerate(loc_list):
pass
可能不是最好的方法,但将两个相等长度的列表放在一个字典中。我确信还有其他更有经验的程序员在for循环中使用字典键分配。也许...
答案 2 :(得分:6)
每个名称只是一个字典键*。
for x in blah:
正是
for vars()['x'] in blah:
*(尽管该词典不需要实现为实际的dict
对象,如果进行了一些优化,例如在函数范围内)。
答案 3 :(得分:5)
是否存在实际有用的情况?
事实上。曾经想要摆脱itertools.combinations
?
def combinations (pool, repeat):
def combinations_recurse (acc, pool, index = 0):
if index < len(acc):
for acc[index] in pool:
yield from combinations_recurse(acc, pool, index + 1)
else:
yield acc
yield from combinations_recurse([pool[0]] * repeat, pool)
for comb in combinations([0, 1], 3):
print(comb)