Python中的赋值、浅拷贝、深拷贝介绍

和很多语言一样,Python中也分为简单赋值、浅拷贝、深拷贝这几种“拷贝”方式。

在学习过程中,一开始对浅拷贝理解很模糊。不过经过一系列的实验后,我发现对这三者的概念有了进一步的了解。

一、赋值

赋值算是这三种操作中最常见的了,我们通过一些例子来分析下赋值操作:

str例

代码如下:

>>> a = 'hello'
>>> b = 'hello'
>>> c = a
>>> [id(x) for x in a,b,c]
[4404120000, 4404120000, 4404120000]

由以上指令中,我们可以发现a, b, c三者的地址是一样的。所以以上赋值的操作就相当于c = a = b = 'hello'。

赋值是系统先给一个变量或者对象(这里是'hello')分配了内存,然后再将地址赋给a, b, c。所以它们的地址是相同的。

list例

代码如下:

>>> a = ['hello']
>>> b = ['hello']
>>> c = a
>>> [id(x) for x in a,b,c]
[4403975952, 4404095096, 4403975952]

但是这种情况却不一样了,a和b的地址不同。为何?

因为str是不可变的,所以同样是'hello'只有一个地址,但是list是可变的,所以必须分配两个地址。

这时,我们希望探究以上两种情况如果 修改值 会如何?

str例

代码如下:

>>> a = 'world'
>>> [id(x) for x in a,b,c]
[4404120432, 4404120000, 4404120000]
>>> print a, b, c
world hello hello

这时a的地址和值变了,但是b, c地址和值都未变。因为str的不可变性,a要重新赋值则需重新开辟内存空间,所以a的值改变,a指向的地址改变。b, c由于'hello'的不变性,不会发生改变。

list例

代码如下:

>>> a[0] = 'world'
>>> [id(x) for x in a,b,c]
[4403975952, 4404095096, 4403975952]
>>> print a, b, c
['world'] ['hello'] ['world']

这时a, c的值和地址均改变,但二者仍相同,b不改变。由于list的可变性,所以修改list的值不需要另外开辟空间,只需修改原地址的值。所以a, c均改变。

在了解了以上的不同点之后,我们就能很好地分析浅拷贝和深拷贝了。

我们均用list作为例子。

二、浅拷贝


代码如下:

>>> a = ['hello', [123, 234]]
>>> b = a[:]
>>> [id(x) for x in a,b]
[4496003656, 4496066752]
>>> [id(x) for x in a]
[4496091584, 4495947536]
>>> [id(x) for x in b]
[4496091584, 4495947536]

Line3,4可以看出a, b地址不同,这符合list是可变的,应开辟不同空间。那浅拷贝就是拷贝了一个副本吗?再看Line5 - 8,我们发现a, b中元素的地址是相同的。如果说字符串'hello'地址一致还能理解,但是第二个元素是list地址仍一致。 这就说明了浅拷贝的特点,只是将容器内的元素的地址复制了一份 。

接着我们尝试修改a, b中的值:

代码如下:

>>> a[0] = 'world'
>>> a[1].append(345)
>>> print 'a = ', a, '\n\r', 'b = ', b
a =  ['world', [123, 234, 345]]
b =  ['hello', [123, 234, 345]]

a中第一个元素str改变,但是b中未改变;a中第二个元素改变,b中也改变。这就符合不可变的对象修改会开辟新的空间,可变的对象修改不会开辟新空间。也进一步证明了 浅拷贝仅仅是复制了容器中元素的地址 。

三、深拷贝


代码如下:

>>> from copy import deepcopy
>>> a = ['hello', [123, 234]]
>>> b = deepcopy(a)
>>> [id(x) for x in a, b]
[4496066824, 4496066680]
>>> [id(x) for x in a]
[4496091584, 4496067040]
>>> [id(x) for x in b]
[4496091584, 4496371792]

深拷贝后,可以发现a, b地址以及a, b中元素地址均不同。这才是完全 拷贝了一个副本 。

修改a的值后:

代码如下:

>>> a[0] = 'world'
>>> a[1].append(345)
>>> print 'a = ', a, '\n\r', 'b = ', b
a =  ['world', [123, 234, 345]]
b =  ['hello', [123, 234]]

从Line4,5中可以发现仅仅a修改了,b没有任何修改。 因为b是一个完全的副本,元素地址均与a不同,a修改,b不受影响 。

总结:

1. 赋值是将一个对象的地址赋值给一个变量,让变量指向该地址( 旧瓶装旧酒 )。

2. 浅拷贝是在另一块地址中创建一个新的变量或容器,但是容器内的元素的地址均是源对象的元素的地址的拷贝。也就是说新的容器中指向了旧的元素( 新瓶装旧酒 )。

3. 深拷贝是在另一块地址中创建一个新的变量或容器,同时容器内的元素的地址也是新开辟的,仅仅是值相同而已,是完全的副本。也就是说( 新瓶装新酒 )。

(0)

相关推荐

  • Python中的复制操作及copy模块中的浅拷贝与深拷贝方法

    程序中常常需要复制一个对象, 按思路应该是这样的 a = [1, 2, 3] b = a # [1, 2, 3] print b 已经复制好了,但是现在得改变一下第一个元素的值把它改成5 b[0] = 5 # [5, 2, 3] print b # [5, 2, 3] print a 我改变了b的第一个元素的值,但是a的值也改变了,这是因为python中的=是引用.a和b指向的是相同的列表,所以改变列表会出现以上的结果. 解决方法是切片操作 a = [1, 2, 3] b = a[:] b[0]

  • 浅谈Python浅拷贝、深拷贝及引用机制

    这礼拜碰到一些问题,然后意识到基础知识一段时间没巩固的话,还是有遗忘的部分,还是需要温习,这里做份笔记,记录一下 前续 先简单描述下碰到的题目,要求是写出2个print的结果 可以看到,a指向了一个列表list对象,在Python中,这样的赋值语句,其实内部含义是指a指向这个list所在内存地址,可以看作类似指针的概念. 而b,注意,他是把a对象包裹进一个list,并且乘以5,所以b的样子应该是一个大list,里面元素都是a 而当a对象进行了append操作后,其实,隐含的意思是,内存中的这个l

  • Python基础教程之浅拷贝和深拷贝实例详解

    Python基础教程之浅拷贝和深拷贝实例详解            网上关于Python的深拷贝和浅拷贝的文章很多,这里对三种拷贝进行比较并附实例,大家可以参考下 一般的复制 #encoding:utf-8 #定义一个嵌套集合 lista=[1,2,3,[4,5,6,[7,8,9]]] listb=lista #分别打印出 lista和listb的地址值 print id(lista) #4511103096 print id(listb) #4511103096 #修改lista中的内容,li

  • Python中的深拷贝和浅拷贝详解

    要说清楚Python中的深浅拷贝,需要搞清楚下面一系列概念: 变量-引用-对象(可变对象,不可变对象)-切片-拷贝(浅拷贝,深拷贝) [变量-对象-引用] 在Python中一切都是对象,比如说:3, 3.14, 'Hello', [1,2,3,4],{'a':1}...... 甚至连type其本身都是对象,type对象 Python中变量与C/C++/Java中不同,它是指对象的引用,Python是动态类型,程序运行时候,会根据对象的类型 来确认变量到底是什么类型. 单独赋值: 比如说: 复制代

  • Python 拷贝对象(深拷贝deepcopy与浅拷贝copy)

    1. copy.copy 浅拷贝 只拷贝父对象,不会拷贝对象的内部的子对象.2. copy.deepcopy 深拷贝 拷贝对象及其子对象一个很好的例子: Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->import copya = [1, 2, 3, 4, ['a', 'b']]  #原始对象b = a  #赋值,传对象的引用c = copy.c

  • Python对象的深拷贝和浅拷贝详解

    本文内容是在<Python核心编程2>上看到的,感觉很有用便写出来,给大家参考参考! 浅拷贝 首先我们使用两种方式来拷贝对象,一种是切片,另外一种是工厂方法.然后使用id函数来看看它们的标示符 复制代码 代码如下: # encoding=UTF-8   obj = ['name',['age',18]] a=obj[:] b=list(obj) for x in obj,a,b:     print id(x)   35217032 35227912 29943304 他们的id都不同,按照正

  • Python浅拷贝与深拷贝用法实例

    本文实例讲述了Python浅拷贝与深拷贝用法.分享给大家供大家参考.具体分析如下: >>> person=['name',['savings',100]] >>> hubby=person[:] >>> wifey=list(person) >>> [id(x) for x in person,hubby,wifey] [3074051788L, 3074061740L, 3074061996L] >>> [id(y

  • 深入理解python中的浅拷贝和深拷贝

    在讲什么是深浅拷贝之前,我们先来看这样一个现象: a = ['scolia', 123, [], ] b = a[:] b[2].append(666) print a print b 为什么我只对b进行修改,却影响到了a呢?看过我在之前的文章中就说过:序列中保存的都是内存的引用. 所以,当我们通过b去修改里面的空列表的时候,其实就是修改内存中的同一个对象,所以会影响到a. a = ['scolia', 123, [], ] b = a[:] print id(a), id(a[0]), id(

  • Python基础之赋值,浅拷贝,深拷贝的区别

    一.赋值 不会开辟新的内存空间,只是复制了新对象的引用.所以当一个数据发生变化时,另外一个数据也会随之改变. 二. 浅拷贝 创建新对象,其内容是对原对象的引用.浅拷贝之所以称为浅拷贝,是因为它仅仅只拷贝了第一层,即只拷贝了最外层的对象本身,内部的元素都只是拷贝了一个引用而已,即内部元素如果被修改,则另外一个数据也会发生变化. 浅拷贝的三种形式: A = [1, 2, 3, 4] 切片操作 # 第1种 B = A[:] # 第2种 B = [a for a in A] 工厂函数 B = list(

  • Python中字典的浅拷贝与深拷贝用法实例分析

    本文实例讲述了Python中字典的浅拷贝与深拷贝用法.分享给大家供大家参考,具体如下: 最近发现的一个很值得记录的东西就是python字典的浅拷贝问题 首先,明确一下什么是浅拷贝,什么是深拷贝: 简单的来说就是,在有指针的情况下,浅拷贝只是增加了一个指针指向已经存在的内存,而深拷贝就是增加一个指针并且申请一个新的内存,使这个增加的指针指向这个新的内存 也就是说,在浅拷贝情况下,不同引用指向的是同一块内存,改其中一个引用,那么其他引用也会跟着改变 应用到python 的字典复制过程: # codi

  • js中的赋值 浅拷贝和深拷贝详细

    目录 1.js内存 2.赋值 3.浅拷贝 4.深拷贝 前言: 在学习下面文章前我们简单了解一下的内存的知识,以下先简要提一下 1.js内存 js内存,或者说大部分语言的内存都分为栈和堆.基本数据类型的变量值分配在栈上,引用数据类型的变量值分配在堆上,栈中只是存储具体堆中对象的地址. 2.赋值 对于基本数据类型,赋值操作是拷贝,即新旧变量不会相互影响. var a = 1; var b = a; b = 2; console.log(b); // 2 对于引用数据类型,赋值操作只是在栈中新增一个指

  • 浅析Python中的赋值和深浅拷贝

    python中,A object  = B object  是一种赋值操作,赋的值不是一个对象在内存中的空间,而只是这个对象在内存中的位置 . 此时当B对象里面的内容发生更改的时候,A对象也自然而然的会跟着更改. name = ["root","admin"] cp_name = name # 对cp_name进行赋值操作 # 对name列表进行插入 name.append('root_temp') print(name,cp_name) # ['root', 'a

  • Python中的引用与copy介绍

    目录 Python中的引用和copy 1.引用整型数据及列表 2.传递引用 3.copy模块中的copy()和deepcopy() Python中的引用和copy 1.引用整型数据及列表 这里以整型数据类型及列表为例 对于赋值字符串.整型.元组等不可更改数据的变量,其保存的仅是值,改变新变量中的值并不会影响原来变量中的值 origin = 1 new = origin print("new = ",new) new = 2 print("origin = ",ori

  • Python中最神秘missing()函数介绍

    目录 前言 1.有点价值的missing() 2.神出鬼没的missing() 3.被施魔法的missing() 4.小结 前言 一个非常神秘的魔术方法. 这个方法非常不起眼,用途狭窄,我几乎从未注意过它,然而,当发现它可能是上述"定律"的唯一例外情况时,我认为值得再写一篇文章来详细审视一下它. 本文主要关注的问题有: (1) missing()到底是何方神圣? (2) missing()有什么特别之处?擅长"大变活人"魔术? (3) missing()是否真的是上

  • Python中线程锁的使用介绍

    目录 前言 方式一:使用try/finally,确保锁肯定会被释放. 方式二:with语句避免使用try/finally. 总结 前言 当有多个线程,且它们同时访问同一资源时,需要考虑如何避免线程冲突.解决办法是使用线程锁.锁由Python的threading模块提供,并且它最多被一个线程所持有.当一个线程试图获取一个已经锁在资源上的锁时,该线程通常会暂停运行,直到这个锁被释放.看看下面的不具备锁功能的例子: #!/usr/bin/env python3 # -*- coding:utf-8 -

  • Python中安装库的常用方法介绍

    目录 方法一:需要在网络条件下安装 方法二:离线安装 方法三:换源安装 总结 方法一:需要在网络条件下安装 win+R进入运行框输入命令cmd 点击确定进入 普通下载:pip install 模块名字 例如:输入 pip install pygame pip install numpy pip install xlwt xlwt代表需要安装所需包和库等 列出安装版本:pip list 和pip freeze 卸载模块: pip uninstall xlwt Y--确定卸载,n--否 指定版本下载

  • Python中缓存lru_cache的基本介绍和讲解

    目录 一.前言 二.举例说明 三.lru_cache 用法 1.参数详解 2. lru_cache不支持可变参数 四.lru_cache 与redis的区别 五.总结 一.前言 我们经常谈论的缓存一词,更多的类似于将硬盘中的数据存放到内存中以至于提高读取速度,比如常说的redis,就经常用来做数据的缓存.Python的缓存(lru_cache)是一种装饰在被执行的函数上,将其执行的结果缓存起来,当下次请求的时候,如果请求该函数的传参未变则直接返回缓存起来的结果而不再执行函数的一种缓存装饰器. 那

  • Python中python-nmap模块的使用介绍

    目录 一.python-nmap的安装 二.python-nmap模块的使用 1.portScanner()类 环境: python 2.7.13 Windows和Linux默认都是不安装python-nmap的,我们得手动安装 一.python-nmap的安装 linux: wge t http://xael.org/pages/python-nmap-0.6.1.tar.gz tar  -zxvf  python-nmap-0.6.1.tar.gz cd  python-nmap-0.6.1

随机推荐