美文网首页
Python,明明只append了一次,为什么所有子列表都变了啊

Python,明明只append了一次,为什么所有子列表都变了啊

作者: FelixZzzz | 来源:发表于2021-09-06 18:00 被阅读0次
    ——Python在嵌套列表的子列表中append元素的问题
    a = [[]] * 3
    a[1].append('x')
    b = [[] for _ in range(3)]
    b[1].append('x')
    

    ab有何不同?有的同学可能会觉得这两种方法的结果是一样的,都是:

    [[],['x'],[]]
    

    然而真实的结果是:

    [['x'], ['x'], ['x']] # a
    [[], ['x'], []] # b
    

    为什么会产生不同的结果呢?明明只append了一次,为什么a中的所有子列表都变了呢?这要从Python的数据模型说起。

    Python中每个对象都有各自的编号、类型和值。一个对象被创建后,它的编号就绝不会改变,你可以将其理解为该对象在内存中的地址。Python还提供了is运算符和id()函数用于比较和查看对象的编号。

    is运算符可以比较两个对象的编号是否相同;id()函数能返回一个代表其编号的整型数。

    Python中有些对象的值可以改变,有些不可以。值可以改变的对象被称为可变对象;值不可以改变的对象就被称为不可变对象。(一个不可变容器对象如果包含对可变对象的引用,当后者的值改变时,前者的值也会改变;但是该容器仍属于不可变对象,因为它所包含的对象集是不会改变的。因此,不可变并不严格等同于值不能改变,实际含义要更微妙。) 一个对象的可变性是由其类型决定的;例如,数字、字符串和元组是不可变的,而字典和列表是可变的。[1]

    在一开始的问题中,列表a初始化的时候,内层的子列表[]实际上是引用了同一个可变对象。因此对内层的任意一个子列表的append()操作,最终会因为引用了同一个对象的原因,体现在每一个子列表上。

    >>> id(a[0])
    4525967488
    >>> id(a[1])
    4525967488
    >>> id(a[2])
    4525967488
    

    b初始化的时候是创建了3个不同[]对象。每个子列表引用了不同的对象。

    >>> id(b[0])
    4525966272
    >>> id(b[1])
    4525924864
    >>> id(b[2])
    4525906304
    

    Python的官方文档中也说明了这种情况,lst * n这种形式相当于lst与自身进行n次拼接。lst中的项不会被拷贝,而是会进行多次引用。[2]

    我们再深入的看一下刚才括号里面很拗口的那句话:

    “一个不可变容器对象如果包含对可变对象的引用,当后者的值改变时,前者的值也会改变;但是该容器仍属于不可变对象,因为它所包含的对象集是不会改变的。因此,不可变并不严格等同于值不能改变,实际含义要更微妙。”

    简单做个实验:

    >>> a = [1,2]
    >>> b = [3,4]
    >>> c = (a,b)
    >>> c
    ([1, 2], [3, 4])
    
    >>> id(a)
    4302958976
    >>> id(b)
    4302958912
    >>> id(c)
    4302959488
    
    >>> a[0] = 5
    >>> a
    [5, 2]
    >>> id(a)
    4302958976
    >>> id(c)
    4302959488
    >>> c
    ([5, 2], [3, 4])
    

    我们可以看到ab是可变对象,而c是不可变容器对象。我们改变了a的值,可以看到a的编号没有发生改变,这个很正常,因为a是可变对象。
    同时因为c中的元素包括a,所以c的“值”也发生了变化,但是c的编号没有变化,也就是说还是那个不可变对象。也就是说c的不变性是基于它的对象集没有变化。


    1. https://docs.python.org/zh-cn/3/reference/datamodel.html#data-model

    2. https://docs.python.org/zh-cn/3/library/stdtypes.html#common-sequence-operations

    相关文章

      网友评论

          本文标题:Python,明明只append了一次,为什么所有子列表都变了啊

          本文链接:https://www.haomeiwen.com/subject/kimqwltx.html