简体   繁体   English

为什么Python的'for ... in'在值列表和字典列表上的工作方式不同?

[英]Why does Python's 'for … in' work differently on a list of values vs. a list of dictionaries?

I'm wondering about some details of how for ... in works in Python. 我想知道如何在Python中工作的一些细节。

My understanding is for var in iterable on each iteration creates a variable, var, bound to the current value of iterable. 我的理解是每次迭代迭代的for var in iterable都会创建一个变量var,绑定到iterable的当前值。 So, if you do for c in cows; c = cows[whatever] 所以,如果你for c in cows; c = cows[whatever]for c in cows; c = cows[whatever] for c in cows; c = cows[whatever] , but changing c within the loop does not affect the original value. for c in cows; c = cows[whatever] ,但在循环内改变c不会影响原始值。 However, it seems to work differently if you're assigning a value to a dictionary key. 但是,如果要为字典键分配值,它似乎会有所不同。

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

I see one can use enumerate to make the first example work, too, but that's a different story, I guess. 我看到一个人可以使用枚举来使第一个例子工作,但我想这是一个不同的故事。

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]

Why does it affect the original list values in the second example but not the first? 为什么它会影响第二个示例中的原始列表值而不是第一个?

[edit] [编辑]

Thanks for the answers. 谢谢你的回答。 I was looking at this from a PHP point of view, where you can use the & symbol in foreach to specify whether you are operating on a reference to or a copy of the iterable. 我从PHP的角度来看这个,你可以在foreach中使用&符号来指定你是在操作迭代的引用还是副本。 I see now that the real difference is a basic detail of how python works regarding immutable objects. 我现在看到真正的区别是python如何处理不可变对象的基本细节。

It helps to picture what happens to the reference held by c in each iteration: 它有助于描绘每次迭代中c所持有的引用会发生什么:

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

c holds a reference pointing to the first element in the list. c包含指向列表中第一个元素的引用。 When you do c += 2 (ie, c = c + 2 , the temporary variable c is reassigned a new value. This new value is 2 , and c is rebound to this new value. The original list is left alone. 当你做c += 2 (即c = c + 2 ,临时变量c被重新分配一个新值。这个新值是2c被反弹到这个新值。原始列表是单独的。

[ 0, 1, 2, 3, 4, 5 ]

  c -> 2

Now, in the dictionary case, here's what c is bound to during the first iteration: 现在,在字典案例中,这是第一次迭代中c绑定的内容:

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

Here, c points to the dictionary object {'cow':0} . 这里, c指向字典对象{'cow':0} When you do c['cow'] += 2 (ie, c['cow'] = c['cow'] + 2 ), the dictionary object itself is changed, as c is not rebound to an unrelated object. 当你做c['cow'] += 2 (即c['cow'] = c['cow'] + 2 )时,字典对象本身会发生变化,因为c不会反弹到不相关的对象。 That is, c still points to that first dictionary object. 也就是说, c仍然指向第一个字典对象。

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

It's not actually acting differently. 实际上并没有采取不同的行动。 Changing a variable is not the same as changing the attribute of a variable. 更改变量与更改变量的属性不同。 You'll see the same thing in the following example: 您将在以下示例中看到相同的内容:

a = 1
b = a
b = 2 

Here a is still 1. b was assigned a different value and is no longer the same as a 这里a仍然是1. b被赋予了不同的值,并且不再与a相同

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

Here a["hello] returns 2 instead of 1. b is still the same value because we didn't assign anything to b , and thus b is the same as a . We changed the property ["hello"] of b to 2 and since a and b are the same variable a["hello"] is also 2 这里a["hello]返回2,而不是1, b仍是相同的值,因为我们没有指派任何东西b ,因此b是相同的a 。我们改变了财产["hello"]b到2由于ab是相同的变量a["hello"]也是2

c is a temporary, disposable variable in both cases. 在两种情况下, c都是临时的一次性变量。 (Keep in mind that in Python, all variables are merely references, bound to the objects they represent and capable of being rebound to different objects. Python is more consistent than certain other languages in this respect.) (请记住,在Python中,所有变量都只是引用,绑定到它们所代表的对象,并且能够反弹到不同的对象。在这方面,Python比某些其他语言更加一致。)

In your list example, each iteration rebinds c from one integer to another one, leaving the original list unchanged. 在列表示例中,每次迭代都会将c从一个整数重新绑定到另一个整数,而原始列表保持不变。

In your dict example, each iteration accesses the dict to which c is temporarily bound, rebinding one of that dict's members to a different integer. 在你的dict示例中,每次迭代都会访问临时绑定c的dict,将其中一个dict的成员重新绑定到另一个整数。

In both cases, c is ignored at the end of the loop, but since you've changed a data structure other than c in the second case, you notice the changes when the loop is done. 在这两种情况下,在循环结束时都会忽略c ,但由于在第二种情况下您更改了c以外的数据结构,因此在循环完成时会注意到更改。

It's nothing to do with for ... in ... . 这与for ... in ...无关。 Change your code from for c in cows: to c = cows[3] (and dedent the next line) in each example and see the effect. 将代码从for c in cows:更改for c in cows: c = cows[3] cows c = cows[3] (并在下一行中显示)并查看效果。

In your first example, the list elements are int objects; 在第一个示例中,list元素是int对象; they are immutable. 他们是不变的。 In the second example, they are dict objects, which are mutable. 在第二个例子中,它们是dict对象,它们是可变的。

Doing a name assignment as you have in the first loop only rebinds the name. 在第一个循环中执行名称分配仅重新绑定名称。 Doing an item assigment as you have in the second loop modifies the existing object. 在第二个循环中执行项目分配会修改现有对象。

In the second example, you have a list of dictionary objects. 在第二个例子,你有字典对象列表。 c references the dictionary object which is modified inside the loop scope. c引用在循环范围内修改的字典对象。

Regardless of looping, you have to note that: 无论循环如何,您都必须注意:

some_var = some_object

binds the name some_var to the object some_object . 将名称some_var绑定到对象some_object The previous object (if any) referenced by some_var is unbound. some_var引用的上一个对象(如果有)是未绑定的。

some_var[some_index] = some_object

does not bind/unbind some_var ; 不绑定/取消绑定some_var ; it is just syntactic sugar for the following: 它只是以下的语法糖:

some_var.__setitem__(some_index, some_object)

Obviously, some_var still points to the same indexable (a sequence or a mapping) object as before, which has just been modified. 显然, some_var仍然指向与之前相同的可索引(序列或映射)对象,刚刚被修改。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM