为什么在for循环中允许使用列表订阅? [英] Why is using a list subscription allowed in a for loop?

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

问题描述

以下结构在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-loops 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

解压缩序列并将其分配给il[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屋!

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