• Python 浅拷贝和深拷贝


    一、前奏:熟悉Python内存管理

    在Python中,变量在第一次赋值时自动声明,在创建---也就是赋值的时候,解释器会根据语法和右侧的操作数来决定新对象的类型。

    引用计数器:一个内部跟踪变量

    引用计数:每一个对象各有多少个引用

    当对象被创建并(将其引用)赋值给变量时,该对象的引用计数就被设置为 1

    >>> x = 3.14

    语句 x=3.14,创建一个浮点型对象并将其引用赋值给了x,x是第一个引用,该对象的引用计数为1

    当一个对象(的引用)又被赋值到其他变量,或做参数传递等,该对象的一个新的引用(或叫别名)被创建,则该对象的引用计数自动+1。

    以下都会增加引用计数:

    y = x   #做别名
    foo(x)  #做参数传递
    mylis = [1,2,x,'a'] #成为容器对象的一个元素

    以下都会减少引用计数:

    复制代码
    del x   #del显式销毁
    
    bar = x 
    x = True    #对象的一个别名被赋值给其他对象
    
    mylis.remove(x) #对象被从窗口对象中移除
    
    del mylis   #窗口对象本身被销毁
    复制代码

    二、Python的复制

    从上面可见,对象的赋值实际上是对象的引用。当创建一个对象,然后把它赋给另一个变量的时候,python并没有拷贝这个对象,而只是拷贝了这个对象的引用。

    当你对一个对象赋值的时候(做为参数传递,或者做为返回值),Python和Java一样,总是传递原始对象的引用,而不是一个副本。

    复制代码
    """传递原始对象的引用,而不是一个副本"""
    a = [1,2,3]
    b = a
    b.append(100)
    print b         #[1, 2, 3, 100]
    print a         #[1, 2, 3, 100]
    print id(a)     #11530368
    print id(b)     #11530368 
    复制代码

    如 果你想修改一个对象,而且想让原始的对象不受影响,那你就需要对象复制。

    可以 使用copy.copy(),它可以进行对象的浅复制(shallow copy),它复制了对象,但对于对象中的元素,依然使用引用.

    (1)、使用切片[:]操作进行拷贝

    (2)、使用工厂函数(如list/dir/set)等进行拷贝

    (3)、copy.copy()

    复制代码
    >>> jack = ['jack',['age',20]]
    >>> tom = jack[:]
    >>> anny = list(jack)
    >>> jack
    ['jack', ['age', 20]]
    >>> tom
    ['jack', ['age', 20]]
    >>> anny
    ['jack', ['age', 20]]
    >>> print id(jack),id(tom),id(anny)
    13457088 18487376 18489136
    复制代码

    接下来修改上面例子,对姓名和年级进行修改:

    复制代码
    >>> tom[0]='tom'
    >>> anny[0]='anny'
    >>> print tom
    ['tom', ['age', 20]]
    >>> print anny
    ['anny', ['age', 20]]
    >>> anny[1][1]
    20
    >>> anny[1][1]= 18
    >>> anny[1][1]
    18
    >>> print jack,tom,anny
    ['jack', ['age', 18]] ['tom', ['age', 18]] ['anny', ['age', 18]]
    复制代码

    发现,虽然姓名都对号了,但是年龄却都变成了18.这是为什么呢?

    我们看看它们元素的id

    复制代码
    >>> [id(x) for x in jack]
    [13463040, 13456608]
    >>> [id(x) for x in tom]
    [13463424, 13456608]
    >>> [id(x) for x in anny]
    [18501664, 13456608]
    复制代码

    发现,其中列表中  姓名字符串  id都不一样,但是 年龄列表id却都相同。

    这是因为:python中字符串不可以修改,所以在为tom和anny重新命名的时候,会重新创建一个’tom’和’anny’对象,替换旧的’jack’对象。

    这就说明了,浅复制(shallow copy),它复制了对象,但对于对象中的元素,依然使用引用.

    复制代码
    """浅copy"""
    import copy
    aa = [1,2,3]
    bb = copy.copy(aa)
    print id(aa)    #11533088
    print id(bb)    #12014776
    bb[0] =100
    print bb        #[100, 2, 3]
    print aa        #[1,2,3]
    #由于数字不可变,修改的时候会替换旧的对象
    print [id(x) for x in bb]   #[10247196, 10246388, 10246376]
    print [id(y) for y in aa]   #[10246400, 10246388, 10246376]
    复制代码

    下面试试对象中可变元素:

    复制代码
    lis = [['a'],[1,2],['z',23]]
    copyLis = copy.copy(lis)
    copyLis[1].append('bar')
    print copyLis   #[['a'], [1, 2, 'bar'], ['z', 23]]
    print lis       #[['a'], [1, 2, 'bar'], ['z', 23]]
    复制代码

    如果希望复制一个容器对象,以及它里面的所有元素(包含元素的子元素),使用copy.deepcopy,这个方法会消耗一些时间和空间,不过,如果你需要完全复制,这是唯一的方法.

    复制代码
    """深copy"""
    deepLis = copy.deepcopy(lis)
    deepLis[1].append('foo')    
    print deepLis   #[['a'], [1, 2,'foo'], ['z', 23]]
    print lis       #[['a'], [1, 2], ['z', 23]]
    复制代码


    注意:

    1、对于非容器类型(如数字、字符串、和其他‘原子’类型的对象)没有被拷贝一说。

    2、如果元祖变量只包含原子类型对象,则不能深copy。

    3、对于python比较熟悉的人们都应该了解这个事实,在python中,strings, tuples, 和numbers是不可更改的对象,而list,dict等则是可以修改的对象。

    4、我们刚才描述的浅拷贝和深拷贝操作都可以在copy模块中找到。其实copy模块中只有两个函数可用:copy()进行浅拷贝操作,而deepcopy()进行深拷贝操作。

  • 相关阅读:
    java 在线网络考试系统源码 springboot mybaits vue.js 前后分离跨域
    springboot 整合flowable 项目源码 mybiats vue.js 前后分离 跨域
    flowable Springboot vue.js 前后分离 跨域 有代码生成器 工作流
    Flowable 工作流 Springboot vue.js 前后分离 跨域 有代码生成器
    java 企业 网站源码 后台 springmvc SSM 前台 静态化 代码生成器
    java 进销存 商户管理 系统 管理 库存管理 销售报表springmvc SSM项目
    基于FPGA的电子计算器设计(中)
    基于FPGA的电子计算器设计(上)
    FPGA零基础学习:SPI 协议驱动设计
    Signal tap 逻辑分析仪使用教程
  • 原文地址:https://www.cnblogs.com/edisonxiang/p/4608415.html
Copyright © 2020-2023  润新知