可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.
回答1:
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 cat
having 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)
回答2:
In python, it's helpful to think of "names" as references to objects. In other words:
cat = []
The name cat
is a reference to an instance of the list. Every object can have multiple references ...
cat = tiger = []
Here cat
and tiger
reference the same list.
cat = []
tiger = cat
Again, cat
and tiger
reference the same list.
Container types (e.g. list
) can hold multiple references to other objects.
cat = []
dog = [cat]
In this case, the first element in dog
is a reference the same list referenced by cat
. So, if I append to the list that is referenced by cat
, the list that is referenced by the first element of dog
will see the change as well (since they're referencing the same instance of list
).
And, of course, how you put the reference into the container doesn't matter:
cat = []
dog = []
dog.append(cat)
回答3:
When you assign a variable to a list
dog = []
the variable "dog" simply stores a reference to the list object, which exists somewhere in the memory.
If you reassign the variable "dog"
dog = [1]
You have created a new list object, and stored a reference to it. The old list object will be garbage collected (properly deleted) if there are no references to it, as it can never be accessed. However, you can store a reference to it in another object
cat.append(dog) # Stores a reference to the old list
dog = [1,1] # Defined another new list
You are inserting dog into cat not by the name "dog", but by a reference to a list object in memory. Now the old "dog" is not garbage collected as it is referenced by cat. Now changes to the list referenced by dog will not affect "cat", as the two lists are different
回答4:
In python everything is an object and think an object as a memory location.
When you append you are modifying the value stored in the object not the memory location.
When you append list dog to cat as a element, this element also point to the same object. In other words there are two elements accessing same objects one is variable 'dog' and other is an element in 'cat'. So any changes you make to the object is visible in both 'dog' and 'cat'. This is what is happening in version 1.
In version 2, after the first append you created a new object and assign it to variable dog. Now variable 'dog' and element in 'cat' are referring to different objects.
If you don't want the changes made to 'dog' are visible to 'cat' use
cat.append(dog[:])