【问题标题】:Why is using a list subscription allowed in a for loop? [duplicate]为什么在 for 循环中允许使用列表订阅? [复制]
【发布时间】:2017-02-26 14:38:56
【问题描述】:

以下构造如何在 Python 中被接受:

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

似乎没有任何抱怨,它愉快地打印出1 2 3 4。这是如何被允许的?它究竟是做什么的?

【问题讨论】:

  • @johnsharpe 你到底是怎么找到它的?我一直在寻找稳定的 20 分钟,但找不到任何东西! :-)
  • 我在 Google 上搜索了 "python subscription in for loop target list",这是第六次点击。
  • 好吧,我当然错过了。嗯,它是另一个路标!

标签: python for-loop syntax


【解决方案1】:

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-loops 的副产品,本质上是为每次迭代执行赋值语句,如参考中所述:

每个项目依次分配到目标列表使用分配的标准规则(请参阅分配语句),然后执行套件。

所以循环的作用是,它从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)的初始加载和值的返回,操作是完全一样的。

【讨论】:

  • 这里的问题不就是解释赋值的顺序和绑定创建吗?这使得 while 示例本身有点无法解释?
  • @pvg no no 我试图强调的问题是,有一个赋值正在进行,因此target_list 中的任何内容都可以作为for-loop 的迭代变量。编写while 是为了显示执行命令序列的友好形式以及它如何包含隐藏分配。
  • 啊,你是对的,我明白你在说什么。我显然患有 python 绑定和范围斯德哥尔摩综合症。
  • @pvg 哈哈,那么您可以查看this nice article,它很好地解释了for-loops 中的范围界定情况:-)
猜你喜欢
  • 2018-01-16
  • 2014-05-31
  • 1970-01-01
  • 1970-01-01
  • 2017-11-03
  • 1970-01-01
  • 2011-12-20
  • 2019-01-09
  • 2017-12-11
相关资源
最近更新 更多