python学习:浅拷贝与深拷贝详解

2023-12-13 08:17:08

一、’ ==’ & ‘is’

’ =='和is是python对象比较常用的两种方式,简单来说,‘ ==‘操作符比较对象之间的值是否相等,如

  • a == b

而’is’操作符比较的是对象的身份标识是否相等,即它们是否是同一个对象,是否指向同一个内存地址,在python中,每个对象的身份标识,都能通过id(object)获得,因此’is’操作符,相当于比较对象之间的id是否相等

a = 10
b = 10

a == b返回的是True
a is b相当于id(a) == id(b)返回的也是True
  • 上述代码,首先python会为10这个值开辟一块内存,然后变量a和b同时指向这块内存区域,即a和b都是指向10这个变量,因此a和b的值相等,id也相等,a == b和 a is b都返回True

不过,需要注意,对于整型数字来说,a is b为True的结论,只适用于-5到256范围内的数字

a = 257
b = 257

这时候,id(a) != id(b),即a is b返回的是False

  • 出于性能优化的考虑,python内部会对-5到256的整型维持一个数组,起到一个缓存的作用,这样,每次试图创建一个-5到256范围内的整型数字时,python都会从这个数组中返回相对应的引用,而不是重新开辟一块新的内存空间;
  • 若超过这个范围,就会开辟两块内存区域,因此指向的内存地址不一样

性能:

  • 操作符’is’的速度效率,通常要由于’==‘,因为is不允许被重载,只需要比较id(val1) 是否等于 id(val2),但是’ ==’操作符不同,python大部分的数据类型都会去重载’ ==‘,对于列表,’ ==',会遍历列表中的元素,比较它们的值和顺序是否相等

二、浅拷贝 & 深拷贝

(一)、浅拷贝

1、实现方式

  • 数据类型本身的构造器
l1 = [1,2,3,4]
l2 = list(l1)

d1 = {1: 'a',2: 'b'}
d2 = dict(d1)
  • 切片
l1 = [1,2,3,4]
l2 = l1[:]
  • copy函数
import copy
l1 = [1,2,3,4]
l2 = copy.copy(l1)

2、实现原理

  • 浅拷贝,是指重新分配一块内存,创建一个新的对象,里面的元素若是嵌套元素,拷贝的只是嵌套元素对象的引用

怎么理解呢?,假如有下述列表

l1 = [1,2,3,[4,5]]
l2 = l1[:]

相当于l2会重新开辟一块内存区域,会存储1,2,3,但是不会存储[4,5],只是存储[4,5]对象的引用,如下图所示

在这里插入图片描述

可以用代码试一下

l1[0] = 'a'
print(l2) 
  • 发现l2中的元素没有发生变化,是因为l1[0]是被拷贝到l2新开辟到的内存区域,修改l1[0]对l2[0]并没有影响
l1[-1].append(6)
print(l2)
  • 发现l2[-1]也发生了变化,是因为l1[-1]和l2[-1]都是指向同一个内存区域,都是同一个对象的引用

因此浅拷贝也可以简单理解,相当于只拷贝第一层的元素,嵌套元素不进行拷贝

(二)、深拷贝

1、实现方式

  • copy函数
l1 = [1,2,3,4]
l2 = copy.deepcopy(l1)

2、实现原理

  • 深拷贝,是指重新分配一块内存,创建一个新的对象,并且将原对象中的元素,以递归的方式,通过创建新的子对象拷贝到新对象中

如下图所示:
在这里插入图片描述

import copy
l1 = [1,2,3,[4,5]]
l2 = copy.deepcopy(l1)

此时修改列表l1

l1[-1].append(6)
print(l2)
  • 发现l2的元素并没有改变,这样看来,深拷贝才是我们平常意义上理解的拷贝,原对象的修改跟现对象没有一点关系

三、问题

1、对于不可变数据类型,使用浅拷贝和深拷贝的结果是一样的吗?
我们知道,对于不可变数据类型,没有提供修改它的方法,是不是可以理解为无论是浅拷贝还是深拷贝返回的结果都是一样的

  • 答案是不一样
  • 虽然是不可变数据类型,但是里面也可能存储可变的数据类型,比如元组中存储列表
import copy
s1 = (1,2,[3,4])
s2 = copy.copy(s1)
s3 = copy.deepcopy(s2)
s1[-1].append(5)
print(s1)
print(s2)
print(s3)
print(s1 is s2) #True
  • 发现s3是没变的,这个很好理解,深拷贝就是不应该变化的
    发现s1和s2是变化的,这个是因为列表是可变的,但是对于不可变数据结构,浅拷贝并没有新建一段内存区域,而是指向原对象的引用

文章来源:https://blog.csdn.net/nzbing/article/details/134950980
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。