栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

Python深拷贝和浅拷贝详解_arrays.copyof 浅拷贝?

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Python深拷贝和浅拷贝详解_arrays.copyof 浅拷贝?

缘由

刚接触python的时候,就听说过浅拷贝(shallow copy)与深拷贝(deep copy)的不同之处,最开始也只能避免“直接赋值”这种“呆瓜”且极易出错的编程行为,在粗浅地使用python编写数量不多的代码的时,函数量较小,一个函数的输出并没有什么机会当做另外一个函数的输入,直接使用自带的浅拷贝函数—object.copy(),出错了就直接换成copy模块中的copy.deepcopy()便也解决了问题,但是最近在学习使用群智能优化算法,大量的函数套用,由于尚未养成良好的编程习惯,有时使用numpy模块有时使用列表(list),逼得我不得不深入研究一下浅拷贝、深拷贝以及numpy中的copy的区别,为了记忆更加牢固便写一篇博客
下面放一下个人学习的连接
Python 直接赋值、浅拷贝、深拷贝——菜鸟教程

对比
名称实质
直接赋值其实就是对象的引用(别名)
浅拷贝(copy)拷贝父对象,不会拷贝对象的内部的子对象
深拷贝(deepcopy)copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象

字典浅拷贝实例

>>>a = {1: [1,2,3]}
>>> b = a.copy()
>>> a, b
({1: [1, 2, 3]}, {1: [1, 2, 3]})
>>> a[1].append(4)
>>> a, b
({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})

深拷贝

>>>import copy
>>> c = copy.deepcopy(a)
>>> a, c
({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})
>>> a[1].append(5)
>>> a, c
({1: [1, 2, 3, 4, 5]}, {1: [1, 2, 3, 4]})

解析如下:

核心就是指针!!!这种被“刻意隐藏”的指针,是初学者的一个大坑,如果没有学过C/C++更加难以理解指针的含义
简单说一下,指针可以理解为是一个标签,当执行a = 1的时候,1就被贴上a这个标签,通过使用a就可以访问1,当执行b = a的时候,也就是直接赋值法,此时1又被贴上了b这个标签,用b也可以访问1,就是下面这个效果:

>>> a = 1
>>> b = a
>>> a,b
(1, 1)

但是当再次使用a = 3的时候,1上a这个标签就被“撕掉”了,3被贴上了a的标签

>>> a = 3
>>> a,b
(3, 1)

了解过之后,可以试试list结构:

>>> a = [1,2,[2,3]]
>>> a[:]
[1, 2, [2, 3]]
>>> b = a[:]
>>> b
[1, 2, [2, 3]]
>>> a[2].append(5)
>>> a
[1, 2, [2, 3, 5]]
>>> b
[1, 2, [2, 3, 5]]
>>> id(a)
1895430236608
>>> id(b)
1895461248448
>>> id(a[2]),id(b[2])
(1895461745920, 1895461745920)

可以看出[:]也可以实现浅拷贝:a与b的地址不同,但是a[2]与b[2]的地址相同

list使用*来建立“类”(类似)array与numpy的array

首先是list错误的*使用方式

>>> wrong = [[0]*3]*3
>>> wrong
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> wrong[0][0] = 10
>>> wrong
[[10, 0, 0], [10, 0, 0], [10, 0, 0]]
>>> id(wrong[0]),id(wrong[1]),id(wrong[2])
(1895461748096, 1895461748096, 1895461748096)

发现每一行的指针都是指向同一个list对象,*相当于一个直接赋值的操作
正确的方式,每一行的指针都是指向不同的list对象

>>> right = [[0]*3 for _ in range(3)]
>>> right
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> right[0][0] = 5
>>> right
[[5, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> id(right[0]),id(right[1]),id(right[2])
(1895461459392, 1895461683008, 1895461746752)

关于numpy的array,大家看到下面的代码可能会有些疑惑,因为array里面貌似也是一个list的子对象,浅拷贝不应该a,b同一个位置的值都应该改变么,可实际上可以看出并没有产生上述猜想结果。可以把他当做一个真正意义上的矩阵,每一个位置的值都是独立的,并没有list子对象这种存在,具体的原因可能需要去看numpy的实现源码,待读者独立去发掘。

>>> import numpy as np
>>> a = np.array([[1,2,3],[4,5,6],[7,8,9]])
>>> b = a.copy()
>>> a
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])
>>> b
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])
>>> a[0][0] = 100
>>> a
array([[100,   2,   3],
       [  4,   5,   6],
       [  7,   8,   9]])
>>> b
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])
最后的最后

看看下面几行代码,为何a,b地址不同,c,d相同呢?这里具体有两个知识点
一是python对于不可变对象与可变对象的存储引用方式的不同,待读者自己去搜索。
二是python对于一定范围内的整数,他们的标签都指向同样一个位置,都是已经预存好的位置。仔细观察c、d、e、f的地址,前面都一致,但是最后四位差距并不是很大:0——3792;1——3824;2——3856,相差32为内存位置,单位我的猜测是Byte,也就是32*8=256bits所能表示的数字,都是存在固定的位置上的,当然这都是我的猜测,因为不同有含义的内存块之间可能存在内存间隔,所以这个256也许是128,或者64也不一定呢。

>>> a = [1,2]
>>> b = [1,2]
>>> id(a),id(b)
(2276089527168, 2274186029824)
>>> c = 1
>>> d = 1
>>> id(c),id(d)
(2276049643824, 2276049643824)
>>> e = 2
>>> id(e)
2276049643856
>>> f = 0
>>> id(f)
2276049643792
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/783474.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号