The reason for the unintuitive behavior of UnboundLocalError 2

Follow the UnboundLocalError Cause of the unintuitive behavior (suppose you read it). Consider the following Python script:

def f(): # a+=1 # 1 aa=a aa+=1 # b+='b' # 2 bb=b bb+='b' c[0]+='c' # 3 c.append('c') cc=c cc.append('c') d['d']=5 # Update 1 d['dd']=6 # Update 1 dd=d # Update 1 dd['ddd']=7 # Update 1 e.add('e') # Update 2 ee=e # Update 2 ee.add('e') # Update 2 a=1 b='b' c=['c'] d={'d':4} # Update 1 e=set(['e']) # Update 2 f() print a print b print c print d # Update 1 print e # Update 2 

The result of the script is:

 1 b ['cc', 'c', 'c'] {'dd': 6, 'd': 5, 'ddd': 7} set(['e']) 

The recorded lines (marked as 1,2) are the lines that will be associated with the UnboundLocalError, and the SO question I refer to explains why. However, line marked 3 works!

By default, lists are copied by reference in Python, so it’s clear that c changes when cc changes. But why does Python allow c to change, first of all, if it does not allow changing a and b directly from the method area?

I don’t see how the fact that by default lists are copied by reference in Python should make this constructive solution inconsistent.

What am I missing people?

UPDATE:

  • For completeness, I also added a dictionary equivalent to the above question, i.e. added source code and marked update # Update
  • For added completeness, I also added a set of equivalents. The complex behavior is actually amazing to me. I expected it to act like a list and a dictionary ...
+4
source share
2 answers

Unlike strings and integers, lists in Python are mutable objects. This means that they are meant to be changed. Line

 c[0] += 'c' 

matches expression

 c.__setitem__(0, c.__getitem__(0) + 'c') 

which does not make any changes to what the name c bound c . Before and after this call, c - the same list - is just the contents of this list that has changed.

If you would say

 c += ['c'] c = [42] 

in the f() function, it would be the same UnboundLocalError , because the second line makes c local name, and the first line is converted to

 c = c + ['c'] 

it is required that the name c already bound to something that (in this local area) does not yet exist.

+3
source

An important thing to think about is: which object is (or b or c) related to? The line a += 1 changes, to which the integer a belongs. Integers are immutable, so when a variable changes from 1 to 2, it is really the same as a = a + 1 , which gives a reference to a completely new integer.

On the other hand, c[0] += 'c' does not change which list c belongs to, it just changes the line to which its first element belongs. Lists change, so the same list can be changed without changing its identifier.

+2
source

All Articles