前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python copy&deepcopy

python copy&deepcopy

原创
作者头像
用户9955628
修改2022-08-25 11:44:21
2270
修改2022-08-25 11:44:21
举报
文章被收录于专栏:python3基础算法python3基础算法
代码语言:javascript
复制
Talk is cheap,show me the code!
a = [1, 2]
b = [3, 4]
c = [a, b]
d = c.copy()
#d = copy.copy(c) 这样写也可
print(id(c), id(d))
print(c)
"""
4478043008 4477684864
可以看到浅拷贝的内存地址不同,不是一个对象
"""
print(id(c[0]),id(c[1]))
print(id(d[0]),id(d[1]))
"""
4439315200 4439315392
4439315200 4439315392
可以看到浅拷贝的子对象还是原来的对象
"""
a.append(-1)
print(c, d)
"""
[[1, 2, -1], [3, 4]] [[1, 2, -1], [3, 4]]
可以看到浅拷贝改变子对象时,会随着子对象的改变而改变
"""
c.append(-2)
print(c, d)
"""
[[1, 2, -1], [3, 4], -2] [[1, 2, -1], [3, 4]]
可以看到d是c的浅拷贝,当改变c时,d不会跟着改变
"""
d.append(-3)
print(c, d)
"""
[[1, 2, -1], [3, 4], -2] [[1, 2, -1], [3, 4], -3]
可以看到d是c的浅拷贝,当改变d时,c也不会跟着改变
"""
import copy

a = [1, 2]
b = [3, 4]
c = [a, b]
d = copy.deepcopy(c)
print(id(c), id(d))
"""
4510807168 4509967488
可以看到深拷贝的内存地址不同,等于new了一个新对象
"""
print(id(c[0]),id(c[1]))
print(id(d[0]),id(d[1]))
"""
4481958848 4482719552
4482842560 4482868032
可以看到深拷贝的子对象也没有被拷贝,内存地址也不同
"""
a.append(0)
print(c,d)
"""
[[1, 2, 0], [3, 4]] [[1, 2], [3, 4]]
可以看到深拷贝及时原来对象的子对象发生变化,深拷贝的对象也不会发生变化
"""
c.append(-1)
d.append(-2)
print(c,d)
"""
[[1, 2, 0], [3, 4], -1] [[1, 2], [3, 4], -2]
可以看到d是c的深拷贝,其中一个发生变化与另一个毫不相干
"""
"""

总结:

  • d=c 赋值,d和c的指向对象在内存中相同
  • d = copy.copy(c) 浅拷贝,d和c是两个独立的对象,但是d会同时拷贝c的子对象,子对象发生变化d就发生变化
  • d = copy.deepcopy(c) 深拷贝,d和c是完全独立的对象,子对象也完全独立,d和c可以说从此毫不相干 关于可变类型和不可变类型的区别: 可变和不可变的标准: 存储在内存中如果内容发生了变化,内存地址不变,就是可变类型,反之则是不可变类型。 python中的可变类型: list、set、dict 不可变类型:int,string,tuple 深拷贝对所有类型都一样,重新递归拷贝一份存储在内存中。 浅拷贝对可变类型进行浅拷贝,对不可变类型不拷贝,只是指向它原来在内存中的地址。

扩展

== 和 is 的区别:

== 和 is 是python中常用的两种比较方式,区别如下:

  • == 比较两个对象的值是否相等
  • is 比较两个对象的身份标识是否相同,是否是同一个对象,是否指向同一个内存地址
  • python中通过id(object)来获取对象在内存中的地址
  • python源码中为-5~256的整型维持了一个数组,每次试图创建这个范围内的数字时,不会从内存中开辟新空间存储,而是直接引用原来内存中已有的对象
  • 实际coding时使用==比is多很多,因为我们更关心对象的值而不是它在内存中的地址,当比较一个变量与一个单例(singleton)时,通常会使用is,典型的例子就是检查一个变量是否为None
  • is的效率比==要高,是因为is不能被重载,所以在使用is时python不需要去寻找程序中是否有其他地方重载了比较操作符,而是直接去比较两个对象的id;==操作符不同之处在于,执行a == b 时,相当于执行
代码语言:javascript
复制
a.__eq__(b)

而python大部分数据类型都会去重载__eq__这个函数,内部的处理会更复杂,例如在列表中,__eq__函数会遍历列表中的所有元素,比较它们的顺序和值是否相等。

  • 可变序列列表可以通过:切片操作完成浅拷贝
代码语言:javascript
复制
l1 = [1,2]
l2 = l1[:]
l1 == l2 
True
l1 is l2
False
  • 对于元组,使用tuple() 或者:浅拷贝时,不会重新在内存中创建,而是直接从原来元组引用
代码语言:javascript
复制
a = (1,2)
b = a[:]
print(id(a))
print(id(b))
print(a is b)
输出:
4487071104
4487071104
True
  • 深拷贝的副作用:如果被拷贝对象中存在指向自身的引用,程序很容易陷入无限循环
代码语言:javascript
复制
import copy

x=[2]
x.append(x)
print(x)

y = copy.deepcopy(x)
print(y)
输出:
[2, [...]]
[2, [...]]

但是我们发现deepcopy后,x中本身存在对自身的引用,x是一个无限嵌套的列表,但是y是x的深拷贝,程序并没有出现stack overflow,原因是深拷贝函数deepcopy中维护了一个字典,记录已经拷贝对象和它的id,拷贝过程中如果字典里已经存储了将要拷贝的对象,则会直接从字典中返回。相应的源码:

代码语言:javascript
复制
def deepcopy(x, memo=None, _nil=[]):
    """Deep copy operation on arbitrary Python objects.

    See the module's __doc__ string for more info.
    """

    if memo is None:
        memo = {}

    d = id(x)
    y = memo.get(d, _nil)
    if y is not _nil:
        return y

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 总结:
  • 扩展
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com