为什么在for循环中允许任意的目标表达式? [英] Why are arbitrary target expressions allowed in for-loops?

查看:168
本文介绍了为什么在for循环中允许任意的目标表达式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不小心写了这样的代码:

  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_NAMEs:

>>> 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆