Python开发【第二章】:深浅拷贝剖析
Python深浅拷贝剖析
Python中,对象的赋值,拷贝(深/浅拷贝)之间是有差异的,如果使用的时候不注意,就可能产生意外的结果。
下面本文就通过简单的例子介绍一下这些概念之间的差别。
一、对象赋值
创建列表变量Alex,变量包含子列表,通过变量Alex给变量lzl赋值,然后对变量Alex的元素进行修改,此时lzl会有什么变化呢?让我们通过内存地址分析两者的变化
# 对象赋值
import copy #import调用copy模块 Alex = ["Alex", 28, ["Python", "C#", "JavaScript"]]
lzl = Alex #直接赋值 # 修改前打印
print(id(Alex))
print(Alex)
print([id(adr) for adr in Alex])
# 输出: 7316664
# ['Alex', 28, ['Python', 'C#', 'JavaScript']]
# [2775776, 1398430400, 7318024]
print(id(lzl))
print(lzl)
print([id(adr) for adr in lzl])
# 输出: 7316664
# ['Alex', 28, ['Python', 'C#', 'JavaScript']]
# [2775776, 1398430400, 7318024] # 对变量进行修改
Alex[0]='Mr.Wu'
Alex[2].append('CSS')
print(id(Alex))
print(Alex)
print([id(adr) for adr in Alex])
# 输出: 7316664
# ['Mr.Wu', 28, ['Python', 'C#', 'JavaScript', 'CSS']]
# [5170528, 1398430400, 7318024]
print(id(lzl))
print(lzl)
print([id(adr) for adr in lzl])
# 输出: 7316664
# ['Mr.Wu', 28, ['Python', 'C#', 'JavaScript', 'CSS']]
# [5170528, 1398430400, 7318024]
通过上面的代码做出如下两图并进行分析:
1、首先,创建了一个名为Alex的变量,这个变量指向一个list列表,从第一张图中可以看到列表中元素的地址(每次运行,结果可能不同)。然后通过变量Alex给变量lzl进行赋值,变量lzl指向Alex指向的内存地址(7316664),所有可以理解为,Python中,对象的赋值都是进行对象引用(内存地址)的传递,被赋值的变量并没有开辟新内存,两个变量共用一个内存地址
2、第二张图中,由于Alex和lzl指向同一个对象(内存地址),所以对Alex的任何修改都会体现在lzl上。这里需要注意的一点是,str是不可变类型,所以当修改元素Alex为Mr.Wu时,内存地址由2775776变为了5170528,list是可变类型,元素['Python', 'C#', 'JavaScript', 'CSS']修改完后,内存地址仍然是7318024,没有发生改变
---------------------------------------------------------------------------------------
二、浅拷贝
创建列表变量Alex,变量包含子列表,通过copy模块的浅拷贝函数copy()对变量Alex进行拷贝,当对Alex进行操作时,此时lzl会如何变化?
# 浅拷贝
import copy #import调用copy模块 Alex = ["Alex", 28, ["Python", "C#", "JavaScript"]]
lzl = copy.copy(Alex) #通过copy模块里面的浅拷贝函数copy() # 修改前打印
print(id(Alex))
print(Alex)
print([id(adr) for adr in Alex])
# 输出: 10462472
# ['Alex', 28, ['Python', 'C#', 'JavaScript']]
# [5462752, 1359960768, 10463232]
print(id(lzl))
print(lzl)
print([id(adr) for adr in lzl])
# 输出: 10201848
# ['Alex', 28, ['Python', 'C#', 'JavaScript']]
# [5462752, 1359960768, 10463232] # 对变量进行修改
Alex[0]='Mr.Wu'
Alex[2].append('CSS')
print(id(Alex))
print(Alex)
print([id(adr) for adr in Alex])
# 输出: 10462472
# ['Mr.Wu', 28, ['Python', 'C#', 'JavaScript', 'CSS']]
# [10151264, 1359960768, 10463232]
print(id(lzl))
print(lzl)
print([id(adr) for adr in lzl])
# 输出: 10201848
# ['Alex', 28, ['Python', 'C#', 'JavaScript', 'CSS']]
# [5462752, 1359960768, 10463232]
通过上面的代码做出如下两图并进行分析:
1、依然使用一个Alex变量,指向一个list类型的对象,list包含一个子list
2、然后,通过copy模块里面的浅拷贝函数copy(),对Alex指向的对象进行浅拷贝,然后浅拷贝生成的新对象赋值给lzl变量,注意此时变量lzl新建了一块内存(10201848),此内存记录了list中元素的地址,对于list中的元素,浅拷贝就会使用原始元素的引用(内存地址)
3、当对Alex进行修改的时候,由于list中第一个元素“Alex”(str)为不可变类型,所以进行修改的后,第一个元素对应的地址变为了10151264。由于list中第三个元素['Python', 'C#', 'JavaScript'](list)为可变类型,修改后地址没有变化。所以最后变量lzl和变量Alex只是第一个元素不一样。
-----------------------------------------------------------------------------------------
注:当我们使用下面的操作的时候,会产生浅拷贝的效果:
- 使用切片[:]操作
- 使用工厂函数(如list/dir/set)
- 使用copy模块中的copy()函数
三、深拷贝
创建列表变量Alex,变量包含子列表,通过copy模块的深拷贝函数deepcopy()对变量Alex进行拷贝,当对Alex进行操作时,此时lzl会如何变化?
#!/usr/bin/env python
# -*- coding:utf-8 -*-
#-Author-Lian
# 深拷贝
import copy #import调用copy模块 Alex = ["Alex", 28, ["Python", "C#", "JavaScript"]]
lzl = copy.deepcopy(Alex) #通过copy模块里面的深拷贝函数deepcopy() # 修改前打印
print(id(Alex))
print(Alex)
print([id(adr) for adr in Alex])
# 输出: 6202712
# ['Alex', 28, ['Python', 'C#', 'JavaScript']]
# [4086496, 1363237568, 6203472]
print(id(lzl))
print(lzl)
print([id(adr) for adr in lzl])
# 输出: 6203032
# ['Alex', 28, ['Python', 'C#', 'JavaScript']]
# [4086496, 1363237568, 6203512] # 对变量进行修改
Alex[0]='Mr.Wu'
Alex[2].append('CSS')
print(id(Alex))
print(Alex)
print([id(adr) for adr in Alex])
# 输出: 6202712
# ['Mr.Wu', 28, ['Python', 'C#', 'JavaScript', 'CSS']]
# [5236064, 1363237568, 6203472]
print(id(lzl))
print(lzl)
print([id(adr) for adr in lzl])
# 输出: 6203032
# ['Alex', 28, ['Python', 'C#', 'JavaScript']]
# [4086496, 1363237568, 6203512]
通过上面深拷贝的代码做出如下两图并进行分析:
1、依然使用一个Alex变量,指向一个list类型的对象,list包含一个子list
2、然后,通过copy模块里面的深拷贝函数deepcopy(),对Alex指向的对象进行深拷贝,然后深拷贝生成的新对象赋值给lzl变量。跟浅拷贝一样,此时变量lzl依然新建了一块内存(6203032),此内存记录了list中元素的地址。但是,对于list中的元素,深拷贝不是简单的使用原始元素的引用(内存地址),对于list第三个元素(['Python', 'C#', 'JavaScript'])重新生成了一个地址(6203512),此时两个变量的第三个元素的内存引用地址不同
3、当对Alex进行修改的时候,由于list中第一个元素“Alex”(str)为不可变类型,所以进行修改的后,第一个元素对应的地址变为了5236064。虽然list中第三个元素['Python', 'C#', 'JavaScript'](list)为可变类型,修改后不会产生新的地址,但是由于Alex和lzl在第三个元素引用的本就不同,所有Alex的修改对lzl不会产生任何影响
-------------------------------------------------------------------------------------
其实,对于拷贝有一些特殊情况:
- 对于非容器类型(如数字、字符串、和其他'原子'类型的对象)没有拷贝这一说
- 也就是说,对于这些类型,"obj is copy.copy(obj)" 、"obj is copy.deepcopy(obj)"
- 如果元祖变量只包含原子类型对象,则不能深拷贝
Python开发【第二章】:深浅拷贝剖析的更多相关文章
- 路飞学城-Python开发-第二章
''' 数据结构: menu = { '北京':{ '海淀':{ '五道口':{ 'soho':{}, '网易':{}, 'google':{} }, '中关村':{ '爱奇艺':{}, '汽车之家' ...
- Python开发——数据结构【深浅拷贝】
浅拷贝 # 浅拷贝只copy一层 s = [3,'Lucy',4,[1,2]] s1 = s.copy() 深拷贝 # 深拷贝——克隆一分 import copy s = [3,'Lucy',4,[1 ...
- Python开发【第二章】:Python深浅拷贝剖析
Python深浅拷贝剖析 Python中,对象的赋值,拷贝(深/浅拷贝)之间是有差异的,如果使用的时候不注意,就可能产生意外的结果. 下面本文就通过简单的例子介绍一下这些概念之间的差别. 一.对象赋值 ...
- ASP.NET自定义控件组件开发 第二章 继承WebControl的自定义控件
原文:ASP.NET自定义控件组件开发 第二章 继承WebControl的自定义控件 第二章 继承于WebControl的自定义控件 到现在为止,我已经写了三篇关于自定义控件开发的文章,很感谢大家的支 ...
- python直接赋值、深浅拷贝实例剖析
根据数据类型分为两部分进行剖析: int.str类型 list.tuple.dict类型等 1. int.str类型 [int类型实例] >>> import copy ...
- python测试开发面试之深浅拷贝
先来道题热热身 a = ('a', 'b','c') c = copy.copy(a) d = copy.deepcopy(a) if c == d: print("c和d的值相等" ...
- python之路(三)-深浅拷贝
深浅拷贝用法来自copy模块. 导入模块:import copy 浅拷贝:copy.copy 深拷贝:deepcopy 字面理解:浅拷贝指仅仅拷贝数据集合的第一层数据,深拷贝指拷贝数据集合的所有层.所 ...
- Android 系统移植与驱动开发--第二章搭建Android环境核心步骤及心得
第二章 搭建Android 开发环境 虽然在这一章中讲的是Android底层开发环境,但是相应伴随的还有Android NDK程序来测试Linux驱动,HAL程序库.底层开发不仅需要交叉编译环境,还要 ...
- [Python笔记][第二章Python序列-复杂的数据结构]
2016/1/27学习内容 第二章 Python序列-复杂的数据结构 堆 import heapq #添加元素进堆 heapq.heappush(heap,n) #小根堆堆顶 heapq.heappo ...
随机推荐
- 用win-acme给windows服务器添加SSL(Let's Encrypt)证书
本文是我今天用win-acme给windows服务器添加SSL(Let's Encrypt)证书的一个过程,主要是给我自己备忘的. 1.首先先在github上下载最新版的win-acme. 下载地址: ...
- 基于Python的GMSSL实现
基于Python的GMSSL实现 团队任务 一.小组讨论对课程设计任务的理解 基于Python的GMSSL实现,即GmSSL开源加密包的python实现,支持其SM2/SM3/SM4等国密(国家商用密 ...
- git 比较两个分支不同的commit
比如我们有 2 个分支:master, dev,现在想查看这两个 branch 的区别,有以下几种方式: undefined 1.查看 dev 有,而 master 中没有的: 1.查看 dev 有, ...
- React拾遗(下)
reconciliation(协调算法) react用于更新DOM的算法.基于两点假设,实现了一个启发的O(n)算法: 两个不同类型的元素将产生不同的树. 通过渲染器附带key属性,开发者可以示意哪些 ...
- pgpool-II 高可用搭建
pgpool-II主备流复制的架设1.环境 OS: CentOS release 6.4 (Final)DB: postgresql 9.3.6pgpool服务器: pgpool 172.16.0.2 ...
- OpenGL ES: (1) OpenGL ES的由来 (转)
1. 电脑是做什么用的? 电脑又被称为计算机,那么最重要的工作就是计算.看过三体的同学都知道, 电脑中有无数纳米级别的计算单元,通过 0 和 1 的转换,完成加减乘除的操作. 2. 是什么使电脑工作? ...
- Ionic4.x 中的 UI 组件(UI Components) 侧边栏ion-menu组件以及底部tabs结合 侧边栏 ion-menu
1.侧边栏 ion-menu 组件的基本使用 1.创建项目 ionic start myApp sidemenu 2.配置项目 属性 作用 可选值 side 配置侧边栏的位置 start end me ...
- 简易的CRM系统案例SpringBoot + thymeleaf + MySQL + MyBatis版本
创建maven项目 pop.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns ...
- 【精华】PHP网站验证码不显示的终结解决方案
PHP网站验证码不显示,这个是个很基础的PHP问题了,不过有点时候会比较让开发者比较头疼了.很多解决方案仅仅考虑到gd2,却忽略了另外一个很重要的因素了,相信在了解本教程之后,验证码不显示基本上就不算 ...
- OpenStack社区中的GO语言之争
1 背景介绍 Swift之前几乎所有的代码都是用Python实现的,但是性能一直不理想, 社区为了解决性能问题,尝试过很多方法,后来发现用Golang语言进行一部分代码重写, 性能得到了一定的提升,社 ...