简体   繁体   English

在Python中追加与重新定义变量

[英]Append vs. redefining variable in Python

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. 我复习了Stack Overflow提出的一些问题,但没有一个包含我正在寻找的答案。 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 ): 版本1(在变量dog上使用append):

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): 版本2(将dog重新定义为其他列表):

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. 我对Python中的append方法的幼稚理解使我期望版本1与版本2具有相同的输出。我不理解为什么添加dog会以任何方式影响变量cat ,除非我通过append或其他方式显式更改了cat方式。

In python, it's helpful to think of "names" as references to objects. 在python中,将“名称”视为对对象的引用会很有帮助。 In other words: 换一种说法:

cat = []

The name cat is a reference to an instance of the list. 名称cat是对列表实例的引用 Every object can have multiple references ... 每个对象可以有多个引用 ...

cat = tiger = []

Here cat and tiger reference the same list. 在此, cattiger引用了相同的列表。

cat = []
tiger = cat

Again, cat and tiger reference the same list. 同样, cattiger引用相同的列表。

Container types (eg list ) can hold multiple references to other objects . 容器类型 (例如list可以容纳对其他对象的多个引用

cat = []
dog = [cat]

In this case, the first element in dog is a reference the same list referenced by cat . 在这种情况下, dog的第一个元素是对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 ). 因此,如果我追加到cat所引用的列表,则dog的第一个元素所引用的列表也将看到更改(因为它们引用的是list的相同实例)。

And, of course, how you put the reference into the container doesn't matter: 而且,当然,如何将引用放入容器并不重要:

cat = []
dog = []
dog.append(cat)

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. 变量“ dog”仅存储对列表对象的引用 ,该引用存在于内存中的某个位置。

If you reassign the variable "dog" 如果您重新分配变量“ 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. 您不是通过名称“ dog”将dog插入cat,而是通过引用内存中的列表对象。 Now the old "dog" is not garbage collected as it is referenced by cat. 现在,旧的“狗”不再是垃圾收集,因为它被cat引用了。 Now changes to the list referenced by dog will not affect "cat", as the two lists are different 现在对dog引用的列表所做的更改将不会影响“ cat”,因为两个列表不同

In python everything is an object and think an object as a memory location. 在python中,一切都是对象,并将对象视为内存位置。

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. 当您将列表狗添加到cat作为元素时,该元素也指向同一对象。 In other words there are two elements accessing same objects one is variable 'dog' and other is an element in 'cat'. 换句话说,有两个元素访问同一对象,一个是变量“ dog”,另一个是“ cat”中的元素。 So any changes you make to the object is visible in both 'dog' and 'cat'. 因此,您对对象所做的任何更改都可以在“狗”和“猫”中看到。 This is what is happening in version 1. 这就是版本1中发生的情况。

In version 2, after the first append you created a new object and assign it to variable dog. 在版本2中,在第一次追加之后,您创建了一个新对象并将其分配给变量dog。 Now variable 'dog' and element in 'cat' are referring to different objects. 现在变量“ dog”和“ cat”中的元素引用了不同的对象。

If you don't want the changes made to 'dog' are visible to 'cat' use 如果您不希望对“ cat”的使用可见对“ dog”所做的更改

cat.append(dog[:]) cat.append(狗[:])

This is a feature of many programming languages, not just python. 这是许多编程语言的功能,而不仅仅是python。 This is more generally called passing by reference (google it). 这通常称为按引用传递(通过 Google 引用 )。

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. 在Python中,可变数据结构通常通过引用(例如列表/字典等)传递,而不可变数据结构(例如元组/字符串/整数)作为副本传递。 So in code snippet #1, when you do cat.append(dog) the first time around, cat now has a reference to the dog list. 因此,在代码片段1中,当您第一次执行cat.append(dog)时,cat现在具有对狗列表的引用。 (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...). (如果您来自C ++ / C背景,我可以“轻松地”将其与指针的概念进行比较:也不是c ++也具有引用,所以我松散地说...)。

If its still complicated, think of cat having the "address" of dog . 如果它仍然很复杂,请考虑cat具有dog的“地址”。 If dog changes, cat will have the same changes. 如果dog变了, cat也会变。

Coming to your code snippet #2... 来到您的代码段#2 ...

When you redefine dog , you are essentially making dog point to a different list altogether. 重新定义dog ,实际上是在将dog指向完全不同的列表。 so [1,1] and [1] are two different lists completely. 因此[1,1][1]完全是两个不同的列表。

To clarify this concept further (of mutable v/s immutable), try a slightly different exercise... 为了进一步阐明这个概念(可变的v / s不可变),请尝试稍作不同的练习...

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(ie the callee of the function). dog在函数调用后发生更改,因为列表包含引用,并且对该引用所做的所有更改都反映在dog(即函数的被调用方)中。 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. strng不会改变,当func_immutable做strng+="1"时,b / c实际上是复制传递的参数strng,然后对其进行修改。 That modification is made to the local variable strng and lost if not returned(as is the case in my code) 该修改是对局部变量strng ,如果未返回,则会丢失(就像我的代码一样)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM