zoukankan      html  css  js  c++  java
  • Python基础【3】:Python中的深浅拷贝解析


    深浅拷贝

    在研究Python的深浅拷贝区别前需要先弄清楚以下的一些基础概念:

    变量--引用--对象(可变对象,不可变对象)

    切片(序列化对象)--拷贝(深拷贝,浅拷贝)

    我是铺垫~

    一、【变量--引用--对象】:

    Python中一切皆对象,甚至连type本身都是type对象。

      1 >>> type(type(n1))
      2 <class 'type'>

    Python中的变量不同于与Java/c/c++不同,它是对象的引用,是动态型变量,数据类型无须提前声明,变量类型是根据对象的类型动态变化。

    例如运行n=9,Python内部先在内存中找块地址来存储9,n只是对这个对象的引用,或者说将指针指向这个对象。

    这里的整数对象9包含了两层含义:

    1、数值为9

    2、一个头部信息,告诉Python这是个int对象【可理解为一个指向int类型的指针】


    image

    小结:

    变量是一个系统表的元素,拥有指向对象的连接的空间;

    对象是被分配的一块内存,用来存储值;

    引用是在变量赋值时自动形成的变量到对象的指针。


    再来看下面的例子:

      1 >>> a = 3
      2 >>> b = a
      3 >>> id(a);id(b)
      4 1627390480
      5 1627390480
      6 >>> del a
      7 >>> b
      8 3
       >>> a = 5
       >>> b
       3

    imageimage

    可以看出,a和b共享引用,指向同一个内存地址(其id完全相同),所以将变量a删除或者将a赋值为4,都对变量b的数值无影响。

    上面的例子有一个局限,就是数值类型对象是不可改变的,如果换成列表和元组对象呢?

    二、【可变对象--不可变对象】

    Python中的可变对象:可以修改的对象,列表和字典。

    Python中的不可变对象:一旦创建就不可以修改的对象,字符串、元组、整数。

      1 >>> L1 = [2,3,4]
      2 >>> L2 = L1
      3 >>> L2
      4 [2, 3, 4]
      5 >>> L1= [5,6]
      6 >>> L2
      7 [2, 3, 4]
      8 >>> L1  = [1,2,3]
      9 >>> L2 = L1
     10 >>> L1[0] = 99
     11 >>> L1;L2
     12 [99, 2, 3]
     13 [99, 2, 3]
     14 #因为对象本身变了

    如此一来,可变对象的变量共享引用就可能会有问题了(原本对源变量的修改,一不小心影响了其他的变量引用)。所以针对这种情景,才有了深浅copy。

    那么如果不想改变上面L2的值有两种作法:切片和copy模块。

      1 >>> L1
      2 [99, 2, 3]
      3 >>> L2 = L1[:]
      4 >>> L1,L2
      5 ([99, 2, 3], [99, 2, 3])
      6 >>> id(L1);id(L2)
      7 3089554433480
      8 3089554433992
      9 >>> L1[0] = 100
     10 >>> L1;L2
     11 [100, 2, 3]
     12 [99, 2, 3]

    拷贝

    切片技术的应用范围:序列对象,即列表、字符串、元组。

    注:序列是一类基本数据类型(字符串/列表/元组)的统称,这些数据类型都含有一些共同的特性。

    切片不能应用于字典,对于字典只有dic.copy()方法和dic.deepcopy()方法。

      1 >>> dic = {'name':'bolen','age':18}
      2 >>> [item for item in dir(dic) if not item.startswith('__')]
      3 ['clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']
      4 
      5 #上面的语法和三元运算一样,x = 1 if n >2 else 3(或x = [2,1][n>2]),都是Python中的语法糖。
      1 >>> import copy
      2 >>> L3 = copy.copy(L1)
      3 >>> L4 = copy.deepcopy(L1)
      4 >>>

    上面铺垫了这么多,就是想说深浅拷贝,既可应用于序列对象也可用于字典。

    【浅拷贝】

    浅拷贝只拷贝顶层对象或者说父级对象。

    【深拷贝】

    深拷贝是拷贝所有对象,包括顶级对象和镶套对象,或者说拷贝所有父对象和子对象。

    闲言少叙,show code:

      1 >>> n1
      2 {'k1': 'lilei', 'k3': ['yy', 456], 'k2': 123}
      3 >>> n4 = n1
      4 >>> n2= copy.copy(n1)
      5 >>> n3 = copy.deepcopy(n1)
      6 >>> n1;n2;n3;n4
      7 {'k1': 'bboo', 'k3': ['yy', 456], 'k2': 123}
      8 {'k1': 'bboo', 'k3': ['yy', 456], 'k2': 123}
      9 {'k1': 'bboo', 'k3': ['yy', 456], 'k2': 123}
     10 {'k1': 'bboo', 'k3': ['yy', 456], 'k2': 123}
     11 >>> id(n1);id(n2);id(n3);id(n4)
     12 3089553992072
     13 3089554432904
     14 3089554359368
     15 3089553992072
     16 #深浅拷贝内存地址和源对象都不一样。
     17 >>> n1['k1'] = 'bolen'
     18 >>> n1;n2;n3;n4
     19 {'k1': 'bolen', 'k3': ['yy', 456], 'k2': 123}
     20 {'k1': 'bboo', 'k3': ['yy', 456], 'k2': 123}
     21 {'k1': 'bboo', 'k3': ['yy', 456], 'k2': 123}
     22 {'k1': 'bolen', 'k3': ['yy', 456], 'k2': 123}
     23 >>> n1['k3'][1]=789
     24 >>> n1;n2;n3;n4
     25 {'k1': 'bolen', 'k3': ['yy', 789], 'k2': 123}
     26 {'k1': 'bboo', 'k3': ['yy', 789], 'k2': 123}
     27 {'k1': 'bboo', 'k3': ['yy', 456], 'k2': 123}
     28 {'k1': 'bolen', 'k3': ['yy', 789], 'k2': 123}
     29 #当有多层镶套时,源对象改变后,浅拷贝和共享引用的值都变了,而深拷贝没有变。


    image

    【总结】

    深浅拷贝都是对源对象的复制,占用不同的内存空间
    如果源对象只有一级目录的话,源做任何改动,不影响深浅拷贝对象
    如果源对象不止一级目录的话,源做任何改动,都要影响浅拷贝,但不影响深拷贝
    序列对象的切片其实是浅拷贝,即只拷贝顶级的对象

  • 相关阅读:
    spl_autoload_register()和__autoload()区别
    编程语言相关科目
    类继承和重写的区别
    心理扭曲和心理变态含义分别是什么?
    java中的静态方法
    《深入理解JavaScript》—— JSON
    《深入理解JavaScript》—— Date
    《深入理解JavaScript》—— 正则表达式
    《深入理解JavaScript》—— 数组
    《深入理解JavaScript》—— 对象与继承(第2层)
  • 原文地址:https://www.cnblogs.com/bolenzhang/p/6965834.html
Copyright © 2011-2022 走看看