代码之家  ›  专栏  ›  技术社区  ›  JAL

为什么python的“for…in”在值列表和字典列表上的工作方式不同?

  •  6
  • JAL  · 技术社区  · 14 年前

    我想知道如何…在python的作品中。

    我的理解是 for var in iterable 在每次迭代中创建一个变量var,绑定到iterable的当前值。所以,如果你这样做 for c in cows; c = cows[whatever] ,但在循环内更改c不会影响原始值。但是,如果您要为字典键赋值,它的工作方式似乎不同。

    cows=[0,1,2,3,4,5]
    for c in cows:
      c+=2
    
    #cows is now the same - [0,1,2,3,4,5]
    
    cows=[{'cow':0},{'cow':1},{'cow':2},{'cow':3},{'cow':4},{'cow':5}]
    for c in cows:
      c['cow']+=2
    
    # cows is now [{'cow': 2}, {'cow': 3}, {'cow': 4}, {'cow': 5}, {'cow': 6}, {'cow': 7}
    #so, it's changed the original, unlike the previous example
    

    我看到一个人也可以用枚举使第一个例子起作用,但我想这是另一回事。

    cows=[0,1,2,3,4,5]
    for i,c in enumerate(cows):
      cows[i]+=1
    
    # cows is now [1, 2, 3, 4, 5, 6]
    

    为什么它会影响第二个示例中的原始列表值,而不是第一个示例中的列表值?

    [编辑]

    谢谢你的回答。我是从PHP的角度来看这个问题的,在这里您可以使用foreach中的&符号来指定您是在引用iterable还是在iterable的副本上操作。现在我看到了真正的区别是关于Python如何处理不可变对象的基本细节。

    7 回复  |  直到 14 年前
        1
  •  4
  •   John Machin Santi    14 年前

    与此无关 for ... in ... . 更改您的代码 for c in cows: c = cows[3] 在每个例子中(并去掉下一行),并看到效果。

    在第一个示例中,list元素是int对象;它们是不可变的。在第二个例子中,它们是dict对象,是可变的。

        2
  •  17
  •   Santa    14 年前

    它有助于想象在 c 在每次迭代中:

    [ 0, 1, 2, 3, 4, 5 ]
      ^
      |
      c
    

    C持有指向列表中第一个元素的引用。当你这样做的时候 c += 2 (即, c = c + 2 ,临时变量 C 重新分配新值。这个新值是 2 C 反弹到这个新值。原始列表将被单独保留。

    [ 0, 1, 2, 3, 4, 5 ]
    
      c -> 2
    

    现在,在字典的例子中,这里是 C 在第一次迭代期间绑定到:

    [ {'cow':0}, {'cow':1}, {'cow':2}, {'cow':3}, {'cow':4}, {'cow':5} ]
         ^
         |
         c
    

    在这里, C 指向Dictionary对象 {'cow':0} . 当你这样做的时候 c['cow'] += 2 (即, c['cow'] = c['cow'] + 2 ),Dictionary对象本身更改为 C 不会反弹到不相关的对象。也就是说, C 仍然指向第一个字典对象。

    [ {'cow':2}, {'cow':1}, {'cow':2}, {'cow':3}, {'cow':4}, {'cow':5} ]
         ^
         |
         c
    
        3
  •  5
  •   Davy8    14 年前

    事实上,这并没有改变。更改变量与更改变量的属性不同。在下面的示例中,您将看到相同的内容:

    a = 1
    b = a
    b = 2 
    

    这里a还是1。B被分配了一个不同的值,不再与A相同

    a = {"hello": 1}
    b = a
    b["hello"] = 2 
    

    在这里 a["hello] 返回2而不是1。 b 仍然是相同的值,因为我们没有为 因此 是一样的 a . 我们改变了财产 ["hello"] 属于 至2及以后 是相同的变量 a["hello"] 也是2

        4
  •  4
  •   ʇsәɹoɈ    14 年前

    c 在这两种情况下都是临时的、一次性的变量。(请记住,在Python中,所有变量都只是引用,绑定到它们所表示的对象,并且能够反弹到不同的对象。在这方面,python比某些其他语言更加一致。)

    在列表示例中,每个迭代都会重新绑定 C 从一个整数到另一个整数,保持原始列表不变。

    在dict示例中,每个迭代都访问要访问的dict C 是临时绑定的,将该dict的一个成员重新绑定到另一个整数。

    在这两种情况下, C 在循环结束时被忽略,但由于更改了 C 在第二种情况下,当循环完成时,您会注意到更改。

        5
  •  3
  •   Ignacio Vazquez-Abrams    14 年前

    像在第一个循环中那样执行名称分配,只会重新绑定名称。像在第二个循环中那样执行项分配,可以修改现有对象。

        6
  •  1
  •   Cesar Alaniz    14 年前

    在第二个示例中,您有一个列表 属于 字典对象。 c 引用在循环范围内修改的Dictionary对象。

        7
  •  1
  •   tzot    14 年前

    无论循环如何,您必须注意:

    some_var = some_object
    

    绑定名称 some_var 对客体 some_object . 由引用的上一个对象(如果有) 某物 是未绑定的。

    some_var[some_index] = some_object
    

    不绑定/解除绑定 某物 ;它只是以下内容的句法糖分:

    some_var.__setitem__(some_index, some_object)
    

    显然, 某物 仍然指向与以前相同的可索引(序列或映射)对象,该对象刚刚被修改。