首先,如果在其他地方已经回答了这个问题,请先道歉我的问题。我回顾了一些 Stack Overflow 建议的问题,但没有一个包含我正在寻找的答案。我也更感兴趣为什么这种情况比任何解决方法都要避免问题。我试着回答我自己的问题,但我只能将问题简化为更简单的问题。无论如何,有人可以帮我理解下面两组代码之间的区别,我可以理解不同输出的推理吗?
版本1(使用变量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] ]
第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] ]
我对Python中append方法的天真理解使我期望版本1具有与版本2相同的输出。我不明白为什么追加dog
会影响任何变量cat
除非我通过追加或其他方式明确更改cat
。
答案 0 :(得分:1)
在python中,将“names”视为对象的引用是有帮助的。换句话说:
cat = []
名称cat
是列表实例的引用。 每个对象都可以有多个引用 ...
cat = tiger = []
此处cat
和tiger
引用相同的列表。
cat = []
tiger = cat
同样,cat
和tiger
引用相同的列表。
容器类型(例如list
)可以包含对其他对象的多个引用。
cat = []
dog = [cat]
在这种情况下,dog
中的第一个元素是cat
引用的相同列表的引用。因此,如果我附加到cat
引用的列表,dog
的第一个元素引用的列表也会看到更改(因为它们引用 list
)的相同实例。
当然,如何将引用放入容器中无关紧要:
cat = []
dog = []
dog.append(cat)
答案 1 :(得分:1)
将变量分配给列表时
dog = []
变量" dog"只需将引用存储到列表对象中,该列表对象位于内存中的某个位置。
如果您重新分配变量" dog"
dog = [1]
您已创建 new 列表对象,并存储了对它的引用。如果没有对它的引用,旧的列表对象将被垃圾收集(正确删除),因为它永远不会被访问。但是,您可以在另一个对象中存储对它的引用
cat.append(dog) # Stores a reference to the old list
dog = [1,1] # Defined another new list
您正在将狗插入猫而不是名称" dog",而是通过引用内存中的列表对象。现在是老狗"不是垃圾收集,因为它被cat引用。现在对狗引用的列表的更改不会影响" cat",因为这两个列表不同
答案 2 :(得分:1)
在python中,一切都是对象,并将对象视为内存位置。
当您追加时,您正在修改存储在对象中的值而不是内存位置。
当您将list dog附加到cat作为元素时,此元素也指向同一个对象。换句话说,有两个元素访问相同的对象,一个是变量' dog'和其他是猫的一个元素。因此,您对该对象所做的任何更改都会在“狗”和“狗”中显示出来。并且' cat'。这就是版本1中发生的事情。
在版本2中,在第一次追加后,您创建了一个新对象并将其分配给变量dog。现在可变的“狗”#39;和“猫”中的元素是指不同的对象。
如果您不希望对“狗”进行更改。对猫来说是可见的使用
cat.append(狗[:])
答案 3 :(得分:1)
这是许多编程语言的一个特性,而不仅仅是python。 这通常被称为通过引用传递(google it)。
在Python中,可变数据结构通常通过引用传递(例如list / dict等),而不可变数据结构(例如元组/字符串/ int)作为副本传递。
因此,在代码片段#1中,当您第一次执行cat.append(dog)
时,cat现在可以引用狗列表。 (如果你来自C ++ / C背景,我可以"松散地"将它与指针的概念进行比较:不是c ++也有引用,所以我松散地说......)。
如果它仍然很复杂,请考虑cat
拥有"地址" dog
。如果dog
发生变化,cat
将发生相同的更改。
来到你的代码片段#2 ......
当您重新定义dog
时,您基本上会使dog
指向另一个列表。所以[1,1]
和[1]
完全是两个不同的列表。
为了进一步澄清这个概念(可变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
更改,因为列表保存了引用,并且对引用所做的所有更改都反映在dog中(即函数的被调用者)。
strng
不会改变,b / c当func_immutable执行strng+="1"
时,它实际上复制了传递的参数strng,然后修改它。对 local 变量strng
进行了修改,如果没有返回则丢失(在我的代码中就是这种情况)