First let me preface my question with an apology if this has been answered somewhere else. I reviewed a few of the questions Stack Overflow suggested, but none of them contained the answer I'm looking for. I am also more interested in why this is happening than any workarounds to avoid the problem. I tried answering my own question, but I was only able to reduce the problem to a simpler one. Anyway, can someone please help me understand the difference between the two sets of code below that way I can understand the reasoning for the different outputs?
Version 1 (using append on the variable dog):
cat = [ ]
dog = [ ]
dog.append(1)
print dog # [1]
print cat # [ ]
cat.append(dog)
print dog # [1]
print cat # [ [1] ]
dog.append(1)
print dog # [1, 1]
print cat # [ [1, 1] ]
dog.append(1)
print dog # [1, 1, 1]
print cat # [ [1, 1, 1] ]
cat.append(dog)
print dog # [1, 1, 1]
print cat # [ [1, 1, 1], [1, 1, 1] ]
Version 2 (redefining dog as a different list):
cat = [ ]
dog = [ ]
dog = [1]
print dog # [1]
print cat # [ ]
cat.append(dog)
print dog # [1]
print cat # [ [1] ]
dog = [1, 1]
print dog # [1, 1]
print cat # [ [1] ]
dog = [1, 1, 1]
print dog # [1, 1, 1]
print cat # [ [1] ]
cat.append(dog)
print dog # [1, 1, 1]
print cat # [ [1], [1, 1, 1] ]
My naïve understanding of the append method in Python led me to expect Version 1 to have the same output as Version 2. I don't understand why appending dog would affect the variable cat in any way unless I explicitly changed cat via append or some other way.
解决方案
This is a feature of many programming languages, not just python.
This is more generally called passing by reference (google it).
In Python, mutable data-structures are generally passed by reference (such as list/dict etc) while immutable data-structures (such as tuples/strings/ints) are passed as copies.
So in code snippet #1, when you do cat.append(dog) the first time around, cat now has a reference to the dog list. (if you are from a C++/C background, I can "loosely" compare this to the concept of pointers: not c++ has reference too, so i say loosely...).
If its still complicated, think of cathaving the "address" of dog. If dog changes, cat will have the same changes.
Coming to your code snippet #2...
When you redefine dog, you are essentially making dog point to a different list altogether. so [1,1] and [1] are two different lists completely.
To clarify this concept further (of mutable v/s immutable), try a slightly different exercise...
def func_mutable(dog):
dog.append(1)
def func_immutable(strng):
strng+="1"
dog = []
print dog #prints []
func_mutable(dog)
print dog #prints [1]
strng = "1"
print strng #prints "1"
func_immutable(strng)
print strng #still prints "1", and not "11"
dog changes after the function call because the list holds the reference and all changes made to the reference are reflected in the dog(i.e. the callee of the function).
strng does not change, b/c when func_immutable does strng+="1", its actually making a copy of the passed parameter strng, and then modifying it. That modification is made to the local variable strng and lost if not returned(as is the case in my code)