高频面试题之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)浅拷贝之后, 内部的值修改, 会影响拷贝之后的结果
相关文章
|
22天前
|
缓存 NoSQL Redis
Python缓存技术(Memcached、Redis)面试题解析
【4月更文挑战第18天】本文探讨了Python面试中关于Memcached和Redis的常见问题,包括两者的基础概念、特性对比、客户端使用、缓存策略及应用场景。同时,文章指出了易错点,如数据不一致和缓存淘汰策略,并提供了实战代码示例,帮助读者掌握这两款内存键值存储系统的使用和优化技巧。通过理解其核心特性和避免常见错误,可以提升在面试中的表现。
28 2
|
22天前
|
API 数据库 数据安全/隐私保护
Flask框架在Python面试中的应用与实战
【4月更文挑战第18天】Django REST framework (DRF) 是用于构建Web API的强力工具,尤其适合Django应用。本文深入讨论DRF面试常见问题,包括视图、序列化、路由、权限控制、分页过滤排序及错误处理。同时,强调了易错点如序列化器验证、权限认证配置、API版本管理、性能优化和响应格式统一,并提供实战代码示例。了解这些知识点有助于在Python面试中展现优秀的Web服务开发能力。
27 1
|
23天前
|
机器学习/深度学习 数据采集 Python
Python机器学习面试:Scikit-learn基础与实践
【4月更文挑战第16天】本文探讨了Python机器学习面试中Scikit-learn的相关重点,包括数据预处理(特征缩放、缺失值处理、特征选择)、模型训练与评估、超参数调优(网格搜索、随机搜索)以及集成学习(Bagging、Boosting、Stacking)。同时,指出了常见错误及避免策略,如忽视数据预处理、盲目追求高精度、滥用集成学习等。掌握这些知识点和代码示例,能帮助你在面试中展现优秀的Scikit-learn技能。
34 5
|
21天前
|
缓存 监控 算法
Python性能优化面试:代码级、架构级与系统级优化
【4月更文挑战第19天】本文探讨了Python性能优化面试的重点,包括代码级、架构级和系统级优化。代码级优化涉及时间复杂度、空间复杂度分析,使用内置数据结构和性能分析工具。易错点包括过度优化和滥用全局变量。架构级优化关注异步编程、缓存策略和分布式系统,强调合理利用异步和缓存。系统级优化则涵盖操作系统原理、Python虚拟机优化和服务器调优,需注意监控系统资源和使用编译器加速。面试者应全面理解这些层面,以提高程序性能和面试竞争力。
18 1
Python性能优化面试:代码级、架构级与系统级优化
|
21天前
|
前端开发 测试技术 C++
Python自动化测试面试:unittest、pytest与Selenium详解
【4月更文挑战第19天】本文聚焦Python自动化测试面试,重点讨论unittest、pytest和Selenium三大框架。unittest涉及断言、TestSuite和覆盖率报告;易错点包括测试代码冗余和异常处理。pytest涵盖fixtures、参数化测试和插件系统,要注意避免过度依赖unittest特性。Selenium的核心是WebDriver操作、等待策略和测试报告生成,强调智能等待和元素定位策略。掌握这些关键点将有助于提升面试表现。
27 0
|
21天前
|
数据采集 存储 JSON
Python爬虫面试:requests、BeautifulSoup与Scrapy详解
【4月更文挑战第19天】本文聚焦于Python爬虫面试中的核心库——requests、BeautifulSoup和Scrapy。讲解了它们的常见问题、易错点及应对策略。对于requests,强调了异常处理、代理设置和请求重试;BeautifulSoup部分提到选择器使用、动态内容处理和解析效率优化;而Scrapy则关注项目架构、数据存储和分布式爬虫。通过实例代码,帮助读者深化理解并提升面试表现。
22 0
|
22天前
|
SQL 中间件 API
Flask框架在Python面试中的应用与实战
【4月更文挑战第18天】**Flask是Python的轻量级Web框架,以其简洁API和强大扩展性受欢迎。本文深入探讨了面试中关于Flask的常见问题,包括路由、Jinja2模板、数据库操作、中间件和错误处理。同时,提到了易错点,如路由冲突、模板安全、SQL注入,以及请求上下文管理。通过实例代码展示了如何创建和管理数据库、使用表单以及处理请求。掌握这些知识将有助于在面试中展现Flask技能。**
18 1
Flask框架在Python面试中的应用与实战
|
23天前
|
数据可视化 Python
Python模型评估与选择:面试必备知识点
【4月更文挑战第17天】本文深入探讨了Python模型评估与选择在面试中的关键点,包括性能度量、过拟合与欠拟合识别、模型比较与选择、模型融合和偏差-方差权衡。强调了避免混淆评估指标、忽视模型验证和盲目追求高复杂度模型的常见错误,并提供相关代码示例,如交叉验证、网格搜索和超参数调优。通过理解这些概念和技巧,可在面试中展示出色的数据科学能力。
33 12
|
23天前
|
机器学习/深度学习 搜索推荐 Python
Python特征工程面试:从理论到实践
【4月更文挑战第17天】本文探讨了Python在数据科学面试中的特征工程,涵盖基础概念如特征选择和提取,实战技能如缺失值和异常值处理,以及特定场景应用。强调避免过度依赖单一方法,忽视数据分布和相关性,以及保持特征工程的可解释性。提供代码示例展示了处理缺失值、标准化、特征选择和异常值检测的基本操作。建议结合业务理解,灵活运用多种方法并注重模型解释性。
26 9
|
23天前
|
数据采集 机器学习/深度学习 数据挖掘
Python数据清洗与预处理面试题解析
【4月更文挑战第17天】本文介绍了Python数据清洗与预处理在面试中的常见问题,包括Pandas基础操作、异常值处理和特征工程。通过示例代码展示了数据读取、筛选、合并、分组统计、离群点检测、缺失值和重复值处理、特征缩放、编码、转换和降维。强调了易错点,如忽视数据质量检查、盲目处理数据、数据隐私保护、过度简化特征关系和忽视模型输入要求。掌握这些技能和策略将有助于在面试中脱颖而出。
33 8