Following up on Reason for unintuitive UnboundLocalError behaviour (I will assume you've 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 commented out lines (marked 1,2) are lines that would through an UnboundLocalError and the SO question I referenced explains why. However, the line marked 3 works!
By default, lists are copied by reference in Python, therefore it's understandable that c changes when cc changes. But why should Python allow c to change in the first place, if it didn't allow changes to a and b directly from the method's scope?
I don't see how the fact that by default lists are copied by reference in Python should make this design decision inconsistent.
What am I missing folks?
UPDATES:
- For completeness I also added the dictionary equivalent to the question above, i.e. I added the source code and marked the update with
# Update
- For further completeness I also added the set equivalent. The set's behavior is actually surprisingly for me. I expected it to act similar to list and dictionary...
The important thing to think about is this: what object does a (or b or c) refer to? The line
a += 1
is changing which integer a refers to. Integers are immutable, so when a changes from 1 to 2, it's really the same asa = a + 1
, which is giving a an entirely new integer to refer to.On the other hand,
c[0] += 'c'
doesn't change which list c refers to, it merely changes which string its first element refers to. Lists are mutable, so the same list can be modified without changing its identity.Unlike strings and integers, lists in Python are mutable objects. This means they are designed to be changed. The line
is identical to saying
which doesn't make any change to what the name
c
is bound to. Before and after this call,c
is the same list – it's just the contents of this list that have changed.Had you said
in the function
f()
, the sameUnboundLocalError
would have occured, because the second line makesc
a local name, and the first line translates torequiring the name
c
to be already bound to something, which (in this local scope) it isn't yet.