高频面试题之Python深拷贝和浅拷贝

简介: 在Python中聊到深拷贝和浅拷贝这个问题就需要考虑Python的数据类型, 考虑到Python的数据类型就需要考虑变量的存储问题, 接下来我们从 变量, 对象, 引用的概念出发, 再到数据赋值操作, 延申到深浅拷贝, 循序渐进的梳理下!

高频面试题之Python深拷贝和浅拷贝

coapy0.jpg

在Python中聊到深拷贝和浅拷贝这个问题就需要考虑Python的数据类型, 考虑到Python的数据类型就需要考虑变量的存储问题, 接下来我们从 变量, 对象, 引用的概念出发, 再到数据赋值操作, 延申到深浅拷贝, 循序渐进的梳理下!

1. 前置概念

变量: 能够存储临时数据的容器

对象: 万物皆对象

变量和对象的关系: 变量是 对象在内存中的存储和地址的抽象表示

引用: 对对象的内存地址的指向

以下是 变量 ,对象地址, 对象存储的指向关系

copy1.jpg

2.变量赋值操作

a = 10
print('a的地址:', id(a))

-- a的地址: 2766914546256
1.当变量 被 = 号赋值的时候, 底层会存储这个变量, 生成对象地址, 存储数值10

copy2.jpg

2. 把a变量的值 = 赋值 给b变量, 对象的值10没有变化, 所以变量b的地址和a是一样的, 都指向同一块内存空间
a = 10
b = a
print('a的地址:', id(a))
print('b的地址:', id(b))

-- a的地址: 2766914546256
-- b的地址: 2766914546256

copy3.jpg

3. 修改以上代码中的a的值, a=20, 此时a的对象地址指向了20的存储位置
a = 10
b = a
a = 20
print('a的地址:', id(a))
print('b的地址:', id(b))


-- a的地址: 2766918794752
-- b的地址: 2766914546256

copy4.jpg

变量赋值操作小结:
通过以上操作可知Python中采用的是引用传递, 如果想让2个变量不再指向同一个内存地址空间, 我们需要使用拷贝来实现

3.拷贝 copy-deepcopy

Python提供了内置模块copy, 通过 copy() 函数实现浅拷贝, 通过deepcopy()函数实现深拷贝

这个时候我们就要考虑2问题: 1. 浅拷贝和深拷贝 2.可变类型和不可变类型

3.1浅拷贝 ---copy

copy---不可变数据类型 int float string tuple
from copy import deepcopy, copy
# 不可变数据类型 int float string tuple
a = 10
print('a的地址:', id(a))
b = copy(a)
a = 20
print('a的地址:', id(a))
print('b的地址:', id(b))

-- a的地址: 2405541964368
-- a的地址: 2405541964688
-- b的地址: 2405541964368  # 浅拷贝a之后 b的地址没有变化
copy---可变数据类型 list dict set
from copy import deepcopy, copy

# 可变数据类型 list dict set --- 就一层
list1 = [1, 2, 3]
list2 = copy(list1)
list1.append(4)

print('list1的地址:', id(list1), list1)
print('list2的地址:', id(list2), list2)

-- list1的地址: 2221170113984 [1, 2, 3, 4]
-- list2的地址: 2221170113728 [1, 2, 3]   # 浅拷贝list1之后, list2地址是新的, 同时内部值不受list1的修改所影响
    
    
    
# 可变数据类型 list dict set --- 嵌套
list1 = [1, 2, 3, ['a', 'b']]
list2 = copy(list1)
list1[3].append(4)

print('list1的地址:', id(list1), list1)
print('list2的地址:', id(list2), list2)
print("list1内部['a', 'b']地址:", id(list1[3]), list1)
print("list2内部['a', 'b']地址:", id(list2[3]), list2)

list1的地址: 2153560030336 [1, 2, 3, ['a', 'b', 4]]
list2的地址: 2153560031360 [1, 2, 3, ['a', 'b', 4]]   # 深拷贝list1之后, list2地址是新的,
list1内部['a', 'b']地址: 2153560030592 [1, 2, 3, ['a', 'b', 4]]
list2内部['a', 'b']地址: 2153560030592 [1, 2, 3, ['a', 'b', 4]] # 深拷贝 里面的嵌套的元素 地址不变,  会受到 原始list1内部数据的改变而改变

3.2 深拷贝--deepcopy

deepcopy--不可变数据类型 int float string tuple
# 不可变数据类型 int float string tuple
a = 10
print('a的地址:', id(a), a)
b = deepcopy(a)
a = 20
print('a的地址:', id(a), a)
print('b的地址:', id(b), b)

a的地址: 1504512666192 10
a的地址: 1504512666512 20
b的地址: 1504512666192 10 # 深拷贝  a之后 b的地址没有变化
deepcopy---可变数据类型 list dict set
from copy import deepcopy, copy

# 可变数据类型 list dict set
list1 = [1, 2, 3]
list2 = deepcopy(list1)
list1.append(4)
print('list1的地址:', id(list1), list1)
print('list2的地址:', id(list2), list2)

list1的地址: 2284475923840 [1, 2, 3, 4]
list2的地址: 2284475923584 [1, 2, 3] # 深拷贝  list1之后, list2地址是新的, 同时内部值不受list1的修改所影响
    
# 可变数据类型 list dict set  嵌套
list1 = [1, 2, 3, ['a', 'b']]
list2 = deepcopy(list1)
list1[3].append(4)

print('list1的地址:', id(list1), list1)
print('list2的地址:', id(list2), list2)
print("list1内部['a', 'b']地址:", id(list1[3]), list1)
print("list2内部['a', 'b']地址:", id(list2[3]), list2)


list1的地址: 1747837926400 [1, 2, 3, ['a', 'b', 4]]
list2的地址: 1747837927360 [1, 2, 3, ['a', 'b']]     # 深拷贝list1之后, list2地址是新的,
list1内部['a', 'b']地址: 1747837926656 [1, 2, 3, ['a', 'b', 4]]
list2内部['a', 'b']地址: 1747837925568 [1, 2, 3, ['a', 'b']] # 深拷贝 里面的嵌套的元素地址也被拷贝了一份, 所以原始数据修改 它不受影响

4. 函数传参

4.1 参数类型是 不可变类型, 是值传递
# 不可变数据类型 int float string tuple
a = 10
print('传递之前:', id(a))

def func(param):
    param = 2 # 修改形参的值
    print("传递之后: ", id(param))

func(a)

-- 传递之前: 2155117832784
-- 传递之后: 2155117832528 # 修改之后值变了, 地址也变化
4.2 参数类型是 可变类型和函数, 是引用传递
# 可变数据类型 list dict set
list1 = [1, 2, 3]
print('传递之前:', id(list1))

def func(param):
    param.append(4) # 修改形参的值
    print("传递之后: ", id(param))

func(list1)

-- 传递之前: 2060275958464
-- 传递之后: 2060275958464 # 修改之后地址没变划

5. 重点结论:

1.不可变类型: 无论是深拷贝还是浅拷贝, 拷贝完后地址不变, 修改原始对象的值, 不影响被拷贝的结果!
2.可变类型:只有一层的时候 , 浅拷贝和深拷贝, 拷贝完后产生新的地址, 修改原始对象的值, 不影响被拷贝的结果!
嵌套多层的时候, 深拷贝是递归拷贝所有曾, 修改原始对象的值和嵌套内部的值, 不影响被拷贝的结果!
3.函数传参:不可变类型是值传递, 可变类型和函数是引用传递
4.注意点: 元组类型比较特殊, 元组内部如果嵌套了 可变类型(list, dict)浅拷贝之后, 内部的值修改, 会影响拷贝之后的结果
相关文章
|
3月前
|
机器学习/深度学习 算法 Java
机器学习、基础算法、python常见面试题必知必答系列大全:(面试问题持续更新)
机器学习、基础算法、python常见面试题必知必答系列大全:(面试问题持续更新)
|
3月前
|
Java C++ Python
【面试宝典】深入Python高级:直戳痛点的题目演示(下)
【面试宝典】深入Python高级:直戳痛点的题目演示(下)
|
3月前
|
设计模式 Unix Python
【面试宝典】深入Python高级:直戳痛点的题目演示(上)
【面试宝典】深入Python高级:直戳痛点的题目演示(上)
|
5月前
|
API Python
python中copy模块的使用,深拷贝和浅拷贝
python中copy模块的使用,深拷贝和浅拷贝
45 0
|
6月前
|
数据采集 机器学习/深度学习 数据可视化
了解数据科学面试中的Python数据分析重点,包括Pandas(DataFrame)、NumPy(ndarray)和Matplotlib(图表绘制)。
【7月更文挑战第5天】了解数据科学面试中的Python数据分析重点,包括Pandas(DataFrame)、NumPy(ndarray)和Matplotlib(图表绘制)。数据预处理涉及缺失值(dropna(), fillna())和异常值处理。使用describe()进行统计分析,通过Matplotlib和Seaborn绘图。回归和分类分析用到Scikit-learn,如LinearRegression和RandomForestClassifier。
113 3
|
5月前
|
存储 JavaScript 前端开发
JS浅拷贝及面试时手写源码
JS浅拷贝及面试时手写源码
|
6月前
|
Python
155. 最小栈 力扣 python 空间换时间 o(1) 腾讯面试题
155. 最小栈 力扣 python 空间换时间 o(1) 腾讯面试题
|
6月前
|
Python
Python 中浅拷贝(copy)和深拷贝(deepcopy)
Python 中浅拷贝(copy)和深拷贝(deepcopy)
|
6月前
|
存储 算法 索引
1124. 表现良好的最长时间段 (python) 前缀和 分类讨论 最大长度 力扣 面试题
1124. 表现良好的最长时间段 (python) 前缀和 分类讨论 最大长度 力扣 面试题
|
Python
93 python高级 - 深拷贝、浅拷贝
93 python高级 - 深拷贝、浅拷贝
33 0
93 python高级 - 深拷贝、浅拷贝