【问题标题】:Python: why does my list change when I'm not actually changing it?Python:为什么我的列表在我没有实际更改时会更改?
【发布时间】:2012-08-13 04:46:26
【问题描述】:

新手有问题,所以请温柔:

list = [1, 2, 3, 4, 5]
list2 = list

def fxn(list,list2):
    for number in list:
        print(number)
        print(list)
        list2.remove(number)
        print("after remove list is  ", list, " and list 2 is  ", list2)
    return list, list2

list, list2 = fxn(list, list2)
print("after fxn list is  ", list)
print("after fxn list2 is  ", list2)

这会导致:

1
[1, 2, 3, 4, 5]
after remove list is   [2, 3, 4, 5]  and list 2 is   [2, 3, 4, 5]
3
[2, 3, 4, 5]
after remove list is   [2, 4, 5]  and list 2 is   [2, 4, 5]
5
[2, 4, 5]
after remove list is   [2, 4]  and list 2 is   [2, 4]
after fxn list is   [2, 4]
after fxn list2 is   [2, 4]

当我只做list2.remove() 而不是list.remove() 时,我不明白为什么列表会发生变化。我什至不确定要使用什么搜索词来弄清楚。

【问题讨论】:

  • 几年前,当我不断地从 scheme/lisp 切换到 python 时,这种情况一直发生在我身上。在我看来,不可变的列表对象更容易理解。
  • 欢迎来到 SO!如果以下任何答案向您展示了问题的解决方案,请不要忘记接受答案。 (您可以通过单击旁边的小复选标记来接受答案)。
  • @AdamGent:这是一个很好的观点。如果您可以编写代码来构建新列表而不是改变它们,那么别名永远不会成为问题。而且您可以使用元组而不是列表来确保您不会意外地发生变异。这并不总是正确的答案(或者因为 Python 列表/元组作为有效数组的效率特性,或者因为转换为不可变算法并不明显并且您的时间有限),但始终值得牢记.
  • @abarnert 你完全正确。 Lisp/Scheme 的特殊之处在于,不可变列表实现(cons 单元)非常快速且具有内存智能。这有点类似于 Java 和 Python 处理字符串的方式。我曾经尝试在 CMUCL 中为数组切换列表,认为这会提高性能,但我几乎没有得到任何提升。对于可变列表通常更快的 Java、Scala 或 Python 而言,情况并非如此。
  • @user1604149 请点击检查按钮接受答案

标签: python list alias


【解决方案1】:

发生这种情况的原因可以在这里找到:

mlist = [1,2,3,4,5]
mlist2 = mlist

第二个语句“指向”mlist2mlist(即,它们都引用 same 列表对象)并且您对其中一个所做的任何更改都会反映在另一个上。

要制作副本,请尝试以下操作(使用切片操作):

mlist = [1,2,3,4,5]
mlist2 = mlist[:]

如果您对切片符号感到好奇,这个 SO 问题 Python Lists(Slice method) 将为您提供更多背景知识。

最后,使用list 作为标识符并不是一个好主意,因为 Python 已经将这个标识符用于它自己的数据结构(这就是我添加“m”的原因变量名前)

【讨论】:

  • 太棒了!我确实隐约记得有关指向同一个对象的变量的阅读,并想知道所有的大惊小怪是什么。现在我知道了。
  • @user1604149 是的,这绝对是您需要注意和考虑的事情。很高兴我的回答有帮助。
  • 这也是一个有趣的问题,因为列表对象正在被修改,因为它正在被迭代,我认为(?)会导致未定义的行为......
  • @mgilson 这是我看到这个时的第一个想法,但我认为 OP 发布的这个问题的核心(底线)是两件事指的是同一个对象。
  • @Levon -- 你是对的。引用同一个对象的两个变量绝对是这里的根本原因。 (解决这个问题,第二个问题也消失了)。我只是觉得这让问题变得更有趣/更复杂:-)
【解决方案2】:

这是因为listlist2 在您完成分配list2=list 后都引用同一个列表。

试试这个看看它们是指相同的对象还是不同的对象:

id(list)
id(list2)

一个例子:

>>> list = [1, 2, 3, 4, 5]
>>> list2 = list
>>> id(list)
140496700844944
>>> id(list2)
140496700844944
>>> list.remove(3)
>>> list
[1, 2, 4, 5]
>>> list2
[1, 2, 4, 5]

如果您确实想创建 list 的副本,以使 list2 不引用原始列表而是列表的副本,请使用切片运算符:

list2 = list[:]

一个例子:

>>> list
[1, 2, 4, 5]
>>> list2
[1, 2, 4, 5]
>>> list = [1, 2, 3, 4, 5]
>>> list2 = list[:]
>>> id(list)
140496701034792
>>> id(list2)
140496701034864
>>> list.remove(3)
>>> list
[1, 2, 4, 5]
>>> list2
[1, 2, 3, 4, 5]

另外,不要使用list作为变量名,因为原来list指的是类型列表,但是通过定义你自己的list变量,你隐藏了原来的list指的是类型列表。示例:

>>> list
<type 'list'>
>>> type(list)
<type 'type'>
>>> list = [1, 2, 3, 4, 5]
>>> list
[1, 2, 3, 4, 5]
>>> type(list)
<type 'list'>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-14
    • 2017-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-19
    相关资源
    最近更新 更多