• python,可变对象,不可变对象,深拷贝,浅拷贝。


    学习整理,若有问题,欢迎指正。

    python 可变对象,不可变对象

    1. 可变对象

    该对象所指定的内存地址上面的值可以被改变,变量被改变后,其所指向的内存地址上面的值,直接被改变,没有发生复制行为,也没有发生开辟新的内存地址行为。

    python可变对象有,列表,字典,set集合

    列如:

    a = ['1','2','3']
    
    print(id(a))
    2275736586376
    
    a.append('1')
    
    print(a)
    ['1', '2', '3', '1']
    
    print(id(a))
    2275736586376

    我们可以看到,在向 a 列表内追加数据的时候,列表 a 前后的id没有发生变化,所以内存地址内,没有开辟新的空间,而是直接在原指定内存地址上面修改的。

    图解:

            2. 不可变对象

    该对象所指定内存中的值不可以被改变,在改变某个对象的值的时候,由于其内存中的值不可以被改变,所以,会把原来的值复制一份再进行改变,这样就会计算机会开辟

    一段新的内存空间来存储新的值,python 不可变对象有 int str float number,tuple,None

    例如:

    c = 'Hello'
    
    print(id(c))
    2237718097848
    
    print(c)
    Hello
    
    c = c + 'World'
    
    print(id(c))
    2237720838960
    
    print(c)
    HelloWorld

    我们可以看到,c变量在追加的时候,两个id是不一样的,所以内存地址会发生变化,此变化为,将c变量所指向的内存,复制一份,然后再做更改。

    图解:

    此时,c的内存是指向 HelloWorld的,若Hello内存,长时间没有变量指定,则垃圾回收机制会自动回收的。

             3. 等于赋值,并不会产生独立的对象单独存在,而是给原来的数据块打上一个新的标签,其中一个标签值被改变的时候,另一个标签也随之改变。

    alist = [1,2,3,['a','b','c']]
    b = alist
    #此处id相同,所以指向同以内存地址
    print(id(alist))
    2030874445768
    print(id(b))
    2030874445768
    #向alist内追加元素
    alist.append(5)
    print(alist)
    [1, 2, 3, ['a', 'b', 'c'], 5]
    print(b)
    [1, 2, 3, ['a', 'b', 'c'], 5]
    #向alist内的列表追加元素
    alist[3].append("d")
    print(alist)
    [1, 2, 3, ['a', 'b', 'c', 'd'], 5]
    print(b)
    [1, 2, 3, ['a', 'b', 'c', 'd'], 5]
    print(id(alist))
    2030874445768
    print(id(b))
    2030874445768

    由上面代码看出,alist所指向 [1,2,3,['a','b','c']] 内存地址,b 也是指定的[1,2,3,['a','b','c']]内存地址(id相同),所以a.append(5)后,alist变化,b也变化

    图解:

    由上图可以看出,无论alist做什么操作,b的内存地址都是指向alist的,所以alist的任何变化,b都会跟着变化。

           4. 浅复制,浅复制分为两种情况

    • 第一种情况,浅复制的值是不可变对象(数值,字符串,元组)时,与等值复制的是一样的,对象的id值与浅复制对象的id是一样的。
    import copy
    
    a = 'abcde'
    b = a
    print(id(a))
    2030878234584
    print(id(b))
    2030878234584
    
    c = copy.copy(a)
    print(id(c))
    2030878234584
    
    #当改变a的值
    a = a + 'fg'
    print(id(a))
    2030878515304
    #由于字符串为不可变对象,所以id(a)会发生变化,但是b,c不会发生变化,还是指向原有内存
    print(id(b))
    2030878234584
    print(id(c))
    2030878234584

    由上面代码可以看出 b = a 其实b,a是指向同一块内存的(id)相同,c=copy.copy(a)也是和a,b指向同一块内存的(id相同),当a发生变化的时候,a会复制原有值,开辟一块新的内存

    出来,此时,c与b还是指向原有内存(观察id)

    图解:

    • 当浅复制的值是可变对象(列表和元组)时会产生一个“不是那么独立的对象”存在。有两种情况:
      • 复制的 对象中无 复杂 子对象,原来值的改变并不会影响浅复制的值,同时浅复制的值改变也并不会影响原来的值。原来值的id值与浅复制原来的值不同。

             当浅复制的值是可变对象(列表,字典)时,改变的值不是 复杂子对象 代码如下:

    list1 = ['1','2','3']
    print(id(list1))
    2030878577608
    
    list2 = list1
    print(id(list2))
    2030878577608
    
    list3 = list1.copy()
    print(id(list2))
    2030878577608
    #上面三者id都一致
    
    #然后在list1后面append一个新的元素,查看变化情况
    list1.append('4')
    print(list1)
    ['1', '2', '3', '4', '4']
    print(id(list1))
    2030878577608
    print(list2)
    ['1', '2', '3', '4', '4']
    print(id(list2))
    2030878577608
    print(list3)
    ['1', '2', '3']
    print(id(list3))
    2030878531400
    #list3的值与list1,list2的值,不一样,且id也不一样了

    当list2 = list1 的时候,完全指向同一块内存空间,list3 = list1.copy()的时候,list3也指向同一块内存空间,当list1发生变化的时候,list2与list1开辟出一块新的空间用来改变,list3还指向原有空间(或者相反)

    图解:

    • 当浅复制的值是可变对象(列表,字典)时,改变的值是 复杂子对象 代码如下:
    l1 = [1,2,['a','b']]
    l2 = l1
    l3 = copy.copy(l2)
    l4 = copy.deepcopy(l3)
    print(id(l1))
    2030878621960
    print(id(l2))
    2030878621960
    print(id(l3))
    2030878622664
    print(id(l4))
    2030878622728
    #id可以看出,l1,l2的id没有改变,但是,l3,l4的id发生了变化
    l1[2].append('a')
    print(id(l1))
    2030878621960
    print(l1)
    [1, 2, ['a', 'b', 'a']]
    print(l3)
    [1, 2, ['a', 'b', 'a']]
    print(l4)
    [1, 2, ['a', 'b']]
    #l1[2].append('a')是改变l1内的可变列表的值,l1的id没有变化,值有变化,l3的值有变化,但是l4的值没有变化。
    l1.append(3)
    print(id(l1))
    2030878621960
    print(l1)
    [1, 2, ['a', 'b', 'a'], 3]
    print(l2)
    [1, 2, ['a', 'b', 'a'], 3]
    print(l3)
    [1, 2, ['a', 'b', 'a']]
    print(l4)
    [1, 2, ['a', 'b']]
    #l1.append(3)是改变l1的值,l1id没有变化,l3的值没有被改变(还是和上一步的值一样),l4与l1初始值一样的。

     当改变 复杂子对象中的元素时,浅拷贝值发生了变化; 当改变的值不是复杂子对象,浅拷贝的值没有发生变化。因为 浅拷贝 ,复杂子对象的保存方式是 作为 引用 方式存储的,所以修改 浅拷贝的值 和原来的值都可以 改变 复杂子对象的值。

    图解:

     

    • 深度拷贝
      • 即将被复制对象完全再复制一遍作为独立的新个体单独存在。所以改变原有被复制对象不会对已经复制出来的新对象产生影响
      • 上图中已经有说明了。

     

  • 相关阅读:
    Use OWIN to Self-Host ASP.NET Web API 2
    PowerShell 中使用json对象的性能比较
    mysql创建utf-8字符集数据库
    url的三个js编码函数escape(),encodeURI(),encodeURIComponent()简介
    PowerShell文件系统(一)前言
    流程控制------if else分支语句
    可变数据类型和不可变数据类型
    python-----运算符及while循环
    数字类型和字符串类型
    python 基础-----数字,字符串,列表,字典类型简单介绍
  • 原文地址:https://www.cnblogs.com/blankdog/p/10070129.html
Copyright © 2020-2023  润新知