为什么在for循环中允许使用列表订阅?

时间:2017-02-26 14:38:57

标签: python for-loop syntax

Python中如何接受以下构造:

l = [1, 2, 3, 4]
for i, l[i] in enumerate(l[:]):
    print(l[i])

似乎没有任何抱怨,并且很高兴地打印出1 2 3 4。这是怎么允许的,它究竟是做什么的?

1 个答案:

答案 0 :(得分:2)

syntax rule for for loops允许迭代变量为target_list中指定的任何变量:

for_stmt ::=  "for" target_list "in" expression_list ":" suite
              ["else" ":" suite]

其中target_list allows表示以下结构:

target_list     ::=  target ("," target)* [","]
target          ::=  identifier
                     | "(" [target_list] ")"
                     | "[" [target_list] "]"
                     | attributeref
                     | subscription
                     | slicing
                     | "*" target

这意味着您还可以执行其他古怪的事情,例如分配到切片:

for l[::-1] in [l, l, l]: pass  

或订阅:

class Foo: a = 20
for Foo.a in range(2): pass

但我真的不知道你为什么要这样做。

这是for-loop s的副产品,基本上为每次迭代执行赋值语句,如参考文献中所述:

  

依次使用分配的标准规则(参见赋值语句)将每个项目分配给目标列表,然后执行该套件。

循环的作用是,它从expression_list获取迭代器,并对target_list中的每个值执行赋值。基本上等同于以下while循环:

it = enumerate(l[:])
while True:
    try:
        i, l[i] = next(it)
        print(l[i])
    except StopIteration:
        break 

dis也可以显示在字节码级别上显示的此行为。使用略微简化的版本:

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命令之后立即执行相关赋值:

             26 UNPACK_SEQUENCE          2
             29 STORE_FAST               0 (i)
             32 LOAD_GLOBAL              1 (l)
             35 LOAD_FAST                0 (i)
             38 STORE_SUBSCR

解压缩序列并将其分配给il[i]

如果您还反汇编dis('i, l[i] = (1, 2)'),您会看到如果忽略元组(1, 2)的初始加载并返回值,则操作完全相同。