前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >搞定面试之图解Python深拷贝浅拷贝

搞定面试之图解Python深拷贝浅拷贝

作者头像
吾非同
发布2021-01-06 12:58:38
3740
发布2021-01-06 12:58:38
举报
文章被收录于专栏:吾非同吾非同

搞定面试之图解Python深拷贝浅拷贝

首先了解一些基本概念

「在Python中一切皆对象」,任何对象都有三个属性:唯一标识、类型、值。

例如一个字符串:

lst= "hello python!"
# 内存地址,唯一标识
print(id(lst))
# 存储类型
print(type(lst))
# 变量值
print(lst)
# 1914884325552
# <class 'str'>
# hello python!

id() ,是Python的一个内置函数,返回对象的唯一标识,用于获取对象的内存地址

「引用」:在 Python 程序中,每个对象都会在内存中申请开辟一块空间来保存该对象,该对象在内存中所在位置的地址被称为引用,使用变量名进行指代。

「赋值」:简单来说就是对于同一个对象,增加一个别名。原理就是将一个对象的地址赋值给一个变量,使得变量指向该内存地址。

a=1
b=a

拷贝的作用:

为了解决数据传递后被修改的问题,就需要拷贝一份副本,即使副本被修改,也不会影响原始数据 。

「不可变类型(字符串、数值型、布尔值)的深浅拷贝」

# 不可变对象的浅拷贝,以数值为例
import copy
a=5
# 浅拷贝
b=copy.copy(a)
print(id(a))
print(id(b))

# 修改a的值
a=10
print(id(a))
print(id(b))
# 140732266387232
# 140732266387232
# 140732266387392
# 140732266387232
# 不可变对象的深拷贝,以数值为例
import copy
a=5
# 深拷贝
b=copy.deepcopy(a)
print(id(a))
print(id(b))

# 修改a的值
a=10
print(id(a))
print(id(b))

# 140732266387232
# 140732266387232
# 140732266387392
# 140732266387232

「综上,不可变对象的拷贝,浅拷贝和深拷贝一样,对象的引用(内存地址)没有发生变化。」

「可变对象的深浅拷贝」

「1.可变对象(列表、字典、集合)的浅拷贝」

#这里以嵌套列表为例
import copy
lsta = [1, 2, 3]
lstb = [1, 2, [3, 4, 5]]
lsta1 = copy.copy(lsta) # 非嵌套浅拷贝
lstb1 = copy.copy(lstb) # 嵌套列表浅拷贝

print(id(lsta))
print(id(lsta1))
print(id(lsta[0]), id(lsta[1]),id(lsta[2]))
print(id(lsta1[0]), id(lsta1[1]), id(lsta1[2]))

# 1373054849088
# 1373054934336
# 140732266387104 140732266387136 140732266387168
# 140732266387104 140732266387136 140732266387168

print(id(lstb))
print(id(lstb1))
print(id(lstb[0]), id(lstb[2]), id(lstb[2][0]))
print(id(lstb1[0]), id(lstb1[2]), id(lstb1[2][0]))

# 2735667449472
# 2735667458368
# 140732266387104 2735667382912 140732266387168
# 140732266387104 2735667382912 140732266387168

浅拷贝在拷贝时,只会copy一层,在内存中开辟一个空间,存放这个copy的列表。更深的层次并没有copy,即第二层用的都是同一个内存。

「2.可变对象(列表、字典、集合)的深拷贝」

import copy
lsta = [1, 2, 3]
lstb = [1, 2, [3, 4, 5]]
lsta1 = copy.deepcopy(lsta) # 非嵌套深拷贝
lstb1 = copy.deepcopy(lstb) # 嵌套列表深拷贝

print(id(lsta))
print(id(lsta1))
print(id(lsta[0]), id(lsta[1]),id(lsta[2]))
print(id(lsta1[0]), id(lsta1[1]), id(lsta1[2]))

# 2310617518336
# 2310617599488
# 140732194690720 140732194690752 140732194690784
# 140732194690720 140732194690752 140732194690784

print(id(lstb))
print(id(lstb1))
print(id(lstb[0]), id(lstb[2]), id(lstb[2][0]))
print(id(lstb1[0]), id(lstb1[2]), id(lstb1[2][0]))

# 2310617599744
# 2310617608704
# 140732194690720 2310617532800 140732194690784
# 140732194690720 2310617608640 140732194690784

综上,深拷贝时,会逐层进行拷贝,遇到可变类型,就开辟一块内存复制下来,遇到不可变类型就沿用之前的引用。

因为不可变数据修改会从新开辟新的空间,所以,深拷贝数据之间的修改都不会相互影响。

总结:

  • 浅拷贝花费时间少,占用内存少,只拷贝顶层数据,拷贝效率高。
  • 对不可变对象拷贝时,浅拷贝和深拷贝的作用是一致的,不开辟新空间,相当于赋值操作。
  • 可变对象浅拷贝时,只拷贝第一层中的引用,如果元素是可变对象,并且被修改,那么拷贝的对象也会发生变化。
  • 可变对象深拷贝时,会逐层进行拷贝,遇到可变类型,就开辟一块内存复制下来。

「补充一个常考知识点」

is== 的区别」

  • is:「比较两个对象的引用(id值)是否相同,即是否指向同一个内存地址」
  • == :「python中的比较操作符,比较两个对象的值是否相等。」
a=1
b=1
print(a == b)
print(a is b)
print(id(a))
print(id(b))
# True
# True
# 140732603438752
# 140732603438752
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b)
print(a is b)
print(id(a))
print(id(b))
# True
# False
# 2926980497856
# 2926981792128

只要 a 和 b 的值相等,a == b 就会返回True,而只有 id(a) 和 id(b) 相等时,a is b 才返回 True。

因为只需要判断两个对象的id是否相同,所以is比较的效率更高,而==默认会调用对象的 __eq__方法,遍历变量中的所有元素是否相同,效率较低。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-12-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 吾非同 微信公众号,前往查看

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

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 搞定面试之图解Python深拷贝浅拷贝
  • 首先了解一些基本概念
    • 拷贝的作用:
    • 「不可变类型(字符串、数值型、布尔值)的深浅拷贝」
    • 「可变对象的深浅拷贝」
      • 总结:
      • 「补充一个常考知识点」
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档