Why does Python 'for ... in' work differently in the list of values ​​compared to the list of dictionaries?

I am interested to know some details about how ... in works in Python.

My understanding of for var in iterable at each iteration creates a var variable that is bound to the current iteration value. So, if you do for c in cows; c = cows[whatever] for c in cows; c = cows[whatever] , but changing c in the loop does not affect the original value. However, it looks like this works differently if you assign 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 changed the original, unlike the previous example 

I see that you can use an enumeration to make the first example work, but this, in my opinion, is a different story.

 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 this affect the initial values ​​of the list in the second example, but not the first?

[edit]

Thanks for answers. I looked at it from a PHP perspective, where you can use the and symbol in foreach to indicate if you are working with a link or a copy of an iteration. Now I see that the real difference is the main detail of how python works with respect to immutable objects.

+6
python iteration
source share
7 answers

This is not related to for ... in ... Change your code from for c in cows: to c = cows[3] (and split the next line) in each example and see the effect.

In the first example, the list items are int objects; they are unchanging. In the second example, they are dict objects that change.

+4
source share

This helps to understand what happens with the link held by c at each iteration:

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

c contains a link pointing to the first item in the list. When you execute c += 2 (that is, c = c + 2 , the temporary variable c reassigns a new value. This is a new value of 2 , and c is a new value. The original list is left alone.

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

Now, in the case of the dictionary, here is what c is connected during the first iteration:

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

Here c points to the dictionary object {'cow':0} . When you execute c['cow'] += 2 (that is, c['cow'] = c['cow'] + 2 ), the dictionary object itself changes because c does not bounce off the unrelated object. That is, c still points to this first dictionary object.

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

In fact, it does not act differently. Changing a variable is not the same as changing a variable attribute. You will see the same in the following example:

 a = 1 b = a b = 2 

Here a is still 1. b is assigned a different value and no longer matches

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

Here a["hello] returns 2 instead of 1. b remains the same value because we do not assign b anything, and thus b matches a . We changed the ["hello"] of b property to 2, and so as a and b are the same variable a["hello"] also 2

+5
source share

c is a temporary, one-time variable in both cases. (Keep in mind that in Python all variables are just references tied to the objects they represent and are able to bounce to various objects. Python is more consistent than some other languages ​​in this regard.)

In your example list, each iteration overloads c from one integer to another, leaving the original list unchanged.

In your dict example, each iteration refers to a dict to which c is temporarily bound, binding one of these dict members to another integer.

In both cases, c ignored at the end of the loop, but since in the second case you changed the data structure other than c , you will notice the changes when the loop is completed.

+4
source share

Performing a naming convention, as in your first loop, only reorders the name. Executing an attribute of an element, as in your second loop, modifies an existing object.

+3
source share

In the second example, you have a list of dictionary objects . c refers to a dictionary object that changes inside the loop area.

+1
source share

Regardless of the cycle, you should notice that:

 some_var = some_object 

binds the name some_var to some_object . The previous object (if any) referenced by some_var has no restrictions.

 some_var[some_index] = some_object 

does not bind / unbind 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 index object (sequence or display) as before which has just been modified.

+1
source share

All Articles