【问题标题】:Why are lists linked in Python in a persistent way?为什么列表在 Python 中以持久的方式链接?
【发布时间】:2013-07-18 12:45:48
【问题描述】:

设置了一个变量。另一个变量设置为第一个。第一个改变值。第二个没有。从一开始,这就是编程的本质。

>>> a = 1
>>> b = a
>>> b = b - 1
>>> b
0
>>> a
1

然后我将其扩展到 Python 列表。声明并附加了一个列表。另一个列表被声明为等于第一个。第二个列表中的值发生变化。奇怪的是,第一个列表中的值虽然没有直接作用,但也会发生变化。

>>> alist = list()
>>> blist = list()
>>> alist.append(1)
>>> alist.append(2)
>>> alist
[1, 2]
>>> blist
[]
>>> blist = alist
>>> alist.remove(1)
>>> alist
[2]
>>> blist
[2]
>>> 

这是为什么?

我如何防止这种情况发生——我希望alist 不会被blist 的更改所困扰(如果你愿意的话,它是不可变的)?

【问题讨论】:

标签: python list immutability


【解决方案1】:

Python 变量实际上是不是变量,而是对对象的引用(类似于 C 中的指针)。在http://foobarnbaz.com/2012/07/08/understanding-python-variables/中对初学者有很好的解释

让自己相信这一点的一种方法是试试这个:

a=[1,2,3]
b=a
id(a)
68617320
id(b)
68617320

id 返回给定对象的内存地址。由于两个列表都相同,这意味着更改一个会影响另一个,因为它们实际上是同一件事。

【讨论】:

  • "memory address" 对于 CPython 是正确的,但对于其他实现不一定正确。但是,这并没有改变这里的概念。
【解决方案2】:

Python 中的变量绑定是这样工作的:您将一个对象分配给一个变量。

a = 4
b = a

两者都指向4

b = 9

现在b 指向其他地方。

列表也完全一样:

a = []
b = a
b = [9]

现在,b 有一个新值,而a 有旧值。

到现在为止,一切都清楚了,可变和不可变对象的行为相同。

现在你的误解来了:它是关于修改对象。

lists 是可变的,所以如果你改变一个列表,修改通过所有存在的变量(“名称绑定”)可见:

a = []
b = a  # the same list
c = [] # another empty one

a.append(3)
print a, b, c # a as well as b = [3], c = [] as it is a different one

d = a[:] # copy it completely
b.append(9)
# now a = b = [3, 9], c = [], d = [3], a copy of the old a resp. b

【讨论】:

  • 我喜欢你清晰的回答。不过,在我看来,这个问题的正确答案必须包括术语“可变”和“不可变”,因为这些是既定术语。
  • @Jan-PhilipGehrcke 当然,这种行为只能在可变对象中观察到;不可变的不能被改变。
【解决方案3】:

发生的情况是您在执行此操作时创建了对同一列表的另一个引用:

blist = alist

因此,blist 引用与 alist 相同的列表。因此,对该单个列表的任何修改都会影响alistblist

如果你想复制整个列表,而不仅仅是创建一个引用,你可以这样做:

blist = alist[:]

其实你可以自己用id()查看引用:

>>> alist = [1,2]
>>> blist = []
>>> id(alist)
411260888
>>> id(blist)
413871960
>>> blist = alist
>>> id(blist)
411260888
>>> blist = alist[:]
>>> id(blist)
407838672

这是来自Python docs.的相关引用:

Python 中的赋值语句不会复制对象,它们会在目标和对象之间创建绑定。对于可变集合或包含可变项的集合,有时需要一个副本,以便可以更改一个副本而不更改另一个副本。

【讨论】:

【解决方案4】:

基于此post

Python 按值传递对对象的引用(如 Java),并且 Python 中的一切都是对象。这听起来很简单,但是你 会注意到某些数据类型似乎表现出按值传递 特征,而其他人似乎表现得像通过引用传递...... 有什么关系?

了解可变和不可变对象很重要。一些 对象,如字符串、元组和数字,是不可变的。改变 它们在函数/方法中将创建一个新实例,并且 函数/方法之外的原始实例不会改变。其他 对象,如列表和字典是可变的,这意味着您可以 就地更改对象。因此,改变一个对象内部 函数/方法也会改变外面的原始对象。

因此,在您的示例中,您使变量 bListaList 指向同一个对象。因此,当您从 bListaList 中删除一个元素时,它会反映在它们都指向的对象中。

【讨论】:

    【解决方案5】:

    简短回答两个你的问题“为什么会这样?”:因为在 Python 中整数是不可变的,而列表是可变的。

    您在 Python 文档中寻找官方参考资料。看看这个部分: http://docs.python.org/2/reference/simple_stmts.html#assignment-statements

    引用后者:

    赋值语句用于将名称(重新)绑定到值和 修改可变对象的属性或项

    我真的很喜欢这句话,以前从未见过。它准确地回答了你的问题。

    最近一篇关于这个主题的好文章是http://nedbatchelder.com/text/names.html,已经在其中一个 cmets 中提到过。

    【讨论】:

      猜你喜欢
      • 2022-01-09
      • 1970-01-01
      • 1970-01-01
      • 2011-06-30
      • 1970-01-01
      • 2014-11-22
      • 2017-10-04
      • 1970-01-01
      • 2011-06-06
      相关资源
      最近更新 更多