(转)(终极改良版)python基础番外——赋值与深浅拷贝

初学编程的小伙伴都会对于深浅拷贝的用法有些疑问,今天我们就结合python变量存储的特性从内存的角度来谈一谈赋值和深浅拷贝~~~

预备知识一——python的变量及其存储

  在详细的了解python中赋值、copy和deepcopy之前,我们还是要花一点时间来了解一下python内存中变量的存储情况。

  在高级语言中,变量是对内存及其地址的抽象。对于python而言,python的一切变量都是对象,变量的存储,采用了引用语义的方式,存储的只是一个变量的值所在的内存地址,而不是这个变量的只本身。

引用语义:在python中,变量保存的是对象(值)的引用,我们称为引用语义。采用这种方式,变量所需的存储空间大小一致,因为变量只是保存了一个引用。也被称为对象语义和指针语义。
值语义:有些语言采用的不是这种方式,它们把变量的值直接保存在变量的存储区里,这种方式被我们称为值语义,例如C语言,采用这种存储方式,每一个变量在内存中所占的空间就要根据变量实际的大小而定,无法固定下来。
值语义和引用语义的区别:
值语义:   死的、 傻的、 简单的、 具体的、 可复制的
引用语义: 活的、 聪明的、 复杂的、 抽象的、 不可复制的

引用语义与值语义详解

我们来看一张简单易懂的图理解一下python的引用语义和C语言值语义在内存中的存储情况,左右两个图,分别表示了python中变量存储与C语言中变量存储区别:

  

预备知识二——各基本数据类型的地址存储及改变情况

  在python中的数据类型包括:bool、int、long、float、str、set、list、tuple、dict等等。我们可以大致将这些数据类型归类为简单数据类型和复杂的数据结构。

我的划分标准是,如果一个数据类型,可以将其他的数据类型作为自己的元素,我就认为这是一种数据结构。数据结构的分类有很多种,但是在Python中常用的只有集合、序列和映射三种结构。对应python中的set、list(tuple、str)、dict;常用的数据类型有int、long、float、bool、str等类型。(其中,str类型比较特别,因为从C语言的角度来说,str其实是一个char的集合,但是这与本文的关联不大,所以我们暂时不谈这个问题)

数据结构及其划分依据

  由于python中的变量都是采用的引用语义,数据结构可以包含基础数据类型,导致了在python中数据的存储是下图这种情况,每个变量中都存储了这个变量的地址,而不是值本身;对于复杂的数据结构来说,里面的存储的也只只是每个元素的地址而已。:

  

  1.数据类型重新初始化对python语义引用的影响

  变量的每一次初始化,都开辟了一个新的空间,将新内容的地址赋值给变量。对于下图来说,我们重复的给str1赋值,其实在内存中的变化如下右图:

           

  从上图我们可以看出,str1在重复的初始化过程中,是因为str1中存储的元素地址由‘hello world‘的地址变成了‘new hello world‘的。

  2.数据结构内部元素变化重对python语义引用的影响

  对于复杂的数据类型来说,改变其内部的值对于变量的影响:

           

  当对列表中的元素进行一些增删改的操作的时候,是不会影响到lst1列表本身对于整个列表地址的,只会改变其内部元素的地址引用。可是当我们对于一个列表重新初始化(赋值)的时候,就给lst1这个变量重新赋予了一个地址,覆盖了原本列表的地址,这个时候,lst1列表的内存id就发生了改变。上面这个道理用在所有复杂的数据类型中都是一样的。

变量的赋值

搞明白了上面的内容,再来探讨变量的赋值,就变得非常简单了。

  1.str的赋值

             

  我们刚刚已经知道,str1的再次初始化(赋值)会导致内存地址的改变,从上图的结果我们可以看出修改了str1之后,被赋值的str2从内存地址到值都没有受到影响。

  看内存中的变化,起始的赋值操作让str1和str2变量都存储了‘hello world’所在的地址,重新对str1初始化,使str1中存储的地址发生了改变,指向了新建的值,此时str2变量存储的内存地址并未改变,所以不受影响。

  2.复杂的数据结构中的赋值

  刚刚我们看了简单数据类型的赋值,现在来看复杂数据结构变化对应内存的影响。

           

  上图对列表的增加修改操作,没有改变列表的内存地址,lst1和lst2都发生了变化。

  对照内存图我们不难看出,在列表中添加新值时,列表中又多存储了一个新元素的地址,而列表本身的地址没有变化,所以lst1和lst2的id均没有改变并且都被添加了一个新的元素。

  简单的比喻一下,我们出去吃饭,lst1和lst2就像是同桌吃饭的两个人,两个人公用一张桌子,只要桌子不变,桌子上的菜发生了变化两个人是共同感受的。

初识拷贝

  我们已经详细了解了变量赋值的过程。对于复杂的数据结构来说,赋值就等于完全共享了资源,一个值的改变会完全被另一个值共享。

  然而有的时候,我们偏偏需要将一份数据的原始内容保留一份,再去处理数据,这个时候使用赋值就不够明智了。python为这种需求提供了copy模块。提供了两种主要的copy方法,一种是普通的copy,另一种是deepcopy。我们称前者是浅拷贝,后者为深拷贝。

  深浅拷贝一直是所有编程语言的重要知识点,下面我们就从内存的角度来分析一下两者的区别。

浅拷贝

  首先,我们来了解一下浅拷贝。浅拷贝:不管多么复杂的数据结构,浅拷贝都只会copy一层。下面就让我们看一张图,来了解一下浅浅拷贝的概念。

     

看上面两张图,我们加入左图表示的是一个列表sourcelist,sourcelist = [‘str1‘,‘str2‘,‘str3‘,‘str4‘,‘str5‘,[‘str1‘,‘str2‘,‘str3‘,‘str4‘,‘str5‘]];

  右图在原有的基础上多出了一个浅拷贝的copylist,copylist = [‘str1‘,‘str2‘,‘str3‘,‘str4‘,‘str5‘,[‘str1‘,‘str2‘,‘str3‘,‘str4‘,‘str5‘]];

  sourcelist和copylist表面上看起来一模一样,但是实际上在内存中已经生成了一个新列表,copy了sourceLst,获得了一个新列表,存储了5个字符串和一个列表所在内存的地址。

我们看下面分别对两个列表进行的操作,红色的框框里面是变量初始化,初始化了上面的两个列表;我们可以分别对这两个列表进行操作,例如插入一个值,我们会发现什么呢?如下所示:

   从上面的代码我们可以看出,对于sourceLst和copyLst列表添加一个元素,这两个列表好像是独立的一样都分别发生了变化,但是当我修改lst的时候,这两个列表都发生了变化,这是为什么呢?我们就来看一张内存中的变化图:

  我们可以知道sourceLst和copyLst列表中都存储了一坨地址,当我们修改了sourceLst1的元素时,相当于用‘sourceChange‘的地址替换了原来‘str1‘的地址,所以sourceLst的第一个元素发生了变化。而copyLst还是存储了str1的地址,所以copyLst不会发生改变。

  当sourceLst列表发生变化,copyLst中存储的lst内存地址没有改变,所以当lst发生改变的时候,sourceLst和copyLst两个列表就都发生了改变。

  这种情况发生在字典套字典、列表套字典、字典套列表,列表套列表,以及各种复杂数据结构的嵌套中,所以当我们的数据类型很复杂的时候,用copy去进行浅拷贝就要非常小心。。。

深拷贝

  刚刚我们了解了浅拷贝的意义,但是在写程序的时候,我们就是希望复杂的数据结构之间完全copy一份并且它们之间又没有一毛钱关系,应该怎么办呢?

  我们引入一个深拷贝的概念,深拷贝——即python的copy模块提供的另一个deepcopy方法。深拷贝会完全复制原变量相关的所有数据,在内存中生成一套完全一样的内容,在这个过程中我们对这两个变量中的一个进行任意修改都不会影响其他变量。下面我们就来试验一下。

       

看上面的执行结果,这一次我们不管是对直接对列表进行操作还是对列表内嵌套的其他数据结构操作,都不会产生拷贝的列表受影响的情况。我们再来看看这些变量在内存中的状况:

     

  看了上面的内容,我们就知道了深拷贝的原理。其实深拷贝就是在内存中重新开辟一块空间,不管数据结构多么复杂,只要遇到可能发生改变的数据类型,就重新开辟一块内存空间把内容复制下来,直到最后一层,不再有复杂的数据类型,就保持其原引用。这样,不管数据结构多么的复杂,数据之间的修改都不会相互影响。这就是深拷贝~~

原文地址:https://www.cnblogs.com/xiaoxinfengjixuchui/p/8476885.html

时间: 2024-08-13 20:05:49

(转)(终极改良版)python基础番外——赋值与深浅拷贝的相关文章

Python基础入门(三)深浅拷贝、函数、内置函数、文件处理、三元运算、递归

深浅拷贝 import copy copy.copy() #浅拷贝 copy.deepcopy() #深拷贝 num = 110 copynum = num #赋值 一.数字和字符串 对于 数字 和 字符串 而言,赋值.浅拷贝和深拷贝无意义,因为其永远指向同一个内存地址. 1 import copy 2 #定义变量 数字.字符串 3 n1 = 123 4 #n1 = 'nick' 5 print(id(n1)) 6 7 #赋值 8 n2 = n1 9 print(id(n2)) 10 11 #浅

3D数学读书笔记——矩阵基础番外篇之线性变换

本系列文章由birdlove1987编写,转载请注明出处. 文章链接:http://blog.csdn.net/zhurui_idea/article/details/25102425 前面有一篇文章讨论过多坐标系的问题.有的人可能会问我那么多坐标系,它们之间怎么关联呢?嘿嘿~这次的内容可以为解决这个问题打基础奥! 线性变换基础(3D数学编程中,形式转换经常是错误的根源,所以这部分大家要多多思考,仔细运算) 一般来说,方阵(就是行和列都相等的矩阵)能描述任意的线性变换,所以后面我们一般用方阵来变

python——赋值与深浅拷贝

初学编程的小伙伴都会对于深浅拷贝的用法有些疑问,今天我们就结合python变量存储的特性从内存的角度来谈一谈赋值和深浅拷贝~~~ 预备知识一--python的变量及其存储 在详细的了解python中赋值.copy和deepcopy之前,我们还是要花一点时间来了解一下python内存中变量的存储情况. 在高级语言中,变量是对内存及其地址的抽象.对于python而言,python的一切变量都是对象,变量的存储,采用了引用语义的方式,存储的只是一个变量的值所在的内存地址,而不是这个变量的只本身.  引

python爬虫番外篇(一)进程,线程的初步了解

原文地址https://www.cnblogs.com/zhaof/p/6994991.html 整理这番外篇的原因是希望能够让爬虫的朋友更加理解这块内容,因为爬虫爬取数据可能很简单,但是如何高效持久的爬,利用进程,线程,以及异步IO,其实很多人和我一样,故整理此系列番外篇 一.进程 程序并不能单独和运行只有将程序装载到内存中,系统为他分配资源才能运行,而这种执行的程序就称之为进程.程序和进程的区别在于:程序是指令的集合,它是进程的静态描述文本:进程是程序的一次执行活动,属于动态概念. 例如我们

Python数据结构之----数据存储与深浅拷贝

要深入的了解python的数据结构,就需要先了解一些Python中数据存储的知识,进而理解Python中深浅拷贝的原理,接下来我们进一步的来学习. 一.Python的数据存储 在高级语言(C.C++.Java.Python)中,变量是对内存及其地址的抽象.在Python里,一切变量都是对象,变量的存储采用了引用的方式,存储的只是一个变量值所在的内存地址,而不是变量的本身.即变量保存的是对应数据的地址,我们将这种方式称之为对象的引用.而采取这种存储方式,变量保存的只是一个引用,所以变量所需的存储空

Python 学习日记第六篇 -- 深浅拷贝

深浅拷贝: 1.字符串和数字 import copy #浅拷贝 copy.copy() #深拷贝 copy.deepcopy() ###################################################################################### a1 = "string" #a1 = 11 a2 = a1 a3 = copy.copy(a1) a4 = copy.deepcopy(a1) print(id(a1)) print(

Python 3 学习的第八天——深浅拷贝以及函数

Python 学习的第八小节 写此博客 是为了激励自己,并且将自己的心得以及遇到的问题与人分享 一.学习笔记 1.深浅拷贝 浅copy-- copy.copy()  #shallow copy 使用 b = a.copy() 这样b是对a进行了浅copy  包含了浅copy的一切特点 特点:只copy"一层"  这个一层是指(假如a[ [ 1 , 2 ] , 3 , 4 ])这样,b copy后只能修改 b[1].b[2]的值cai不会影响 a 的值,如果b修改了列表中元素的值,那么就

python中的“赋值与深浅拷贝”

Python中,赋值与拷贝(深/浅拷贝)之间是有差异的,这主要源于数据在内存中的存放问题,本文将对此加以探讨. 1 赋值(添加名字) 赋值不会改变内存中数据存放状态,比如在内存中存在一个名为data的数据,此时若执行语句data_01 = data,则现在该份数据有了两个名称(data和data_01),其余都不发生改变,使用任何一个名称对数据进行操作,那么用另外一个名称拿数据时,数据会呈现之间发生的改变.示例如下: 图中,给列表分配了两个名称a与b,对a做改变时b会同样改变,对b做改变时a也会

Linux网络基础番外篇(IP、MAC、TCP)

linux网络管理 计算机网络 MACMAC(Media Access Control或者Medium Access Control)地址,意译为媒体访问控制.介质访问控制,或称为物理地址.硬件地址,用来定义网络设备的位置.在OSI模型中,第三层网络层负责IP地址,第二层数据链路层则负责MAC地址.因此一个主机会有一个MAC地址,而每个网络位置会有一个专属于它的IP地址.MAC地址(MAC Address)MAC(Medium/Media Access Control)地址,用来表示互联网上每一