列表更改意外地反映在子列表中

  • 问题:
  • 我需要用Python创建一个列表列表,因此我键入了以下内容:

    myList = [[1] * 4] * 3

    列表如下:

    [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]  

    然后我改变了最里面的一个值:

    myList[0][0] = 5

    现在我的列表如下:

    [[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]  

    这不是我想要或期望的。有人能解释一下发生了什么事,以及如何避开它吗?在

  • 答案:
  • 当你写[x]*3时,基本上你得到了一个列表[x,x,x]。也就是说,包含3个对同一x的引用的列表。当您修改这个单一的x时,它可以通过对它的所有三个引用看到:

    x = [1] * 4
    l = [x] * 3
    print(f"id(x): {id(x)}")
    # id(x): 140560897920048
    print(
    f"id(l[0]): {id(l[0])}\n"
    f"id(l[1]): {id(l[1])}\n"
    f"id(l[2]): {id(l[2])}"
    )
    # id(l[0]): 140560897920048
    # id(l[1]): 140560897920048
    # id(l[2]): 140560897920048

    x[0] = 42
    print(f"x: {x}")
    # x: [42, 1, 1, 1]
    print(f"l: {l}")
    # l: [[42, 1, 1, 1], [42, 1, 1, 1], [42, 1, 1, 1]]

    要修复它,您需要确保在每个位置都创建一个新列表。一种方法是

    [[1]*4 for _ in range(3)]

    它将每次重新评估[1]*4,而不是只评估一次,并引用1个列表3次

    您可能想知道为什么*不能像列表理解那样生成独立对象。这是因为乘法运算符*对对象进行操作,而没有看到表达式。当使用*[[1]*4]乘以3时,*只看到1元素列表[[1]*4]的计算结果,而不是[[1]*4表达式文本。*不知道如何复制该元素,也不知道如何重新评估[[1]*4],甚至不知道您想要复制,而且通常情况下,甚至可能没有复制元素的方法

    *唯一的选择是对现有子列表进行新的引用,而不是尝试创建新的子列表。其他任何东西都是不一致的,或者需要对基本语言设计决策进行重大的重新设计

    相反,列表理解在每次迭代中都会重新评估元素表达式。[[1]*4 for n in range(3)]每次重新评估[1]*4,原因相同[x**2 for x in range(3)]每次重新评估x**2。每次对[1]*4的求值都会生成一个新的列表,因此列表理解可以满足您的需要

    顺便说一下,[1]*4也不复制[1]的元素,但这并不重要,因为整数是不可变的。你不能像1.value=2那样把1变成2