Append vs. redefining variable in Python

和自甴很熟 提交于 2020-01-17 04:32:06

问题


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 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)




回答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[:])



来源:https://stackoverflow.com/questions/33222944/append-vs-redefining-variable-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!