python的可变不可变与各种浅拷贝深拷贝规则,一并梳理。

Python一切皆引用

在C++/Java里,int a = 1就是创建变量为a,赋值为1;int b = a就是创建变量b,赋值为a的值。a与b是毫不相干的,即“变量是盒子”,但是这不利于理解Python中的一个变量定义。在Python里,我们把变量视为“一个实际存储的引用”(图源:《流畅的python》)。

所以在python里,a = [1, 2, 3]先分配一块区域写入[1,2,3],再让a来代表它;b = a让b与a代表了同一个东西,即使a本身消失了(比如del a),也仅仅是撕下来一张标签而已,b仍然可以访问这个列表。其他类型也是如此

情况一:直接引用

直接引用即b = a,正如上文所说,不会发生拷贝,只是让b也来代表a代表的区域。此时b就是a,b[0]也就是a[0]。

如果修改了a,等于让a指向其他对象,与列表无关,所以b没有变化;而如果修改a[0](或者使用+=,append等),则修改了列表,b[0]也在变化。

但对于单个数或者元组字符串这种不可变对象,你也可以使用+=,但是他们不支持原地修改,因此实际上会调用a = a + b得到的是一个新对象。如a = (1, 2, 3); b = a; a += (4, 5),此时执行a = a + (4, 5),已经指向新的值了,所以b不会改变。

情况二:复制

有些时候我们只编辑列表或字典的副本,所以需要复制,一般最常见的复制方法有:

b = a[:]
b = _ for _ in a
b = copy(a)
b = a.copy()

这些都叫做浅复制,浅复制的时候发生了什么?

浅复制的逻辑将创建一个新对象,然后将每一个值复制一份放入新对象里,花费线性时间。可以看到复制后b与a完全一致,但是a is b不再成立了,a[0]和b[0]也是不再相关的值,你可以任意修改列表b,都不会影响到a里的四个元素(红蓝橙绿四个小圆)。

情况三:深复制

但是浅复制仍然有不能解决的问题。我们知道python里一切皆引用,图里的小圆不是盒子而是标签!,虽然a与b本身已经分开了,但如果有一个元素仍然是列表,那他们其实还是联系在一起的。



如图,浅复制时执行了b[1]=a[1],但b[1]和a[1]是引用,因此通过他们访问的仍然是同一个可变序列,修改a[i]不会导致b[i]变化,但修改a[1][0]却导致b[1][0]变化。

所以我们引入深复制解决这个问题:

from copy import deepcopy
a = [1, [1, 2, 3], "hello"]
b = deepcopy(a)

深复制的逻辑是,将每一个值复制放进新一个对象里,而如果这一项也表示一个可变的迭代对象(列表,字典,没有特殊定制的自定义类),就将这个对象也复制一份。这样就可以得到一份完全的拷贝。

Python里的引用与拷贝规律的更多相关文章

  1. python变量、引用、拷贝之间的关系

    Python中一切皆为对象,不管是集合变量还是数值型or字符串型的变量都是一个引用,都指向对应内存空间中的对象. 简而言之: 变量直接赋值:新变量本身及其内部的元素都与原变量指向相同的内存空间,并且值 ...

  2. python里的引用、浅拷贝、深拷贝

    参考: 1.http://wsfdl.com/python/2013/08/16/%E7%90%86%E8%A7%A3Python%E7%9A%84%E6%B7%B1%E6%8B%B7%E8%B4%9 ...

  3. Python中的变量、引用、拷贝和作用域

    在Python中,变量是没有类型的,这和以往看到的大部分编辑语言都不一样.在使用变量的时候,不需要提前声明,只需要给这个变量赋值即可.但是,当用变量的时候,必须要给这个变量赋值:如果只写一个变量,而没 ...

  4. python 深入理解 赋值、引用、拷贝、作用域

    在 python 中赋值语句总是建立对象的引用值,而不是复制对象.因此,python 变量更像是指针,而不是数据存储区域, 这点和大多数 OO 语言类似吧,比如 C++.java 等 ~ 1.先来看个 ...

  5. 【Python】列表(数组)的引用和拷贝

    # Python里对象赋值传递的引用 arr=[1,2,3,4,5] newArr=arr arr[1]=9 print('arr='+str(arr)) print('newArr='+str(ne ...

  6. Python 对象的引用计数和拷贝

    Python 对象的引用计数和拷贝 Python是一种面向对象的语言,包括变量.函数.类.模块等等一切皆对象. 在python中,每个对象有以下三个属性: 1.id,每个对象都有一个唯一的身份标识自己 ...

  7. python中的引用传递,可变对象,不可变对象,list注意点

    python中的引用传递 首先必须理解的是,python中一切的传递都是引用(地址),无论是赋值还是函数调用,不存在值传递. 可变对象和不可变对象 python变量保存的是对象的引用,这个引用指向堆内 ...

  8. Python的模块引用和查找路径

    模块间相互独立相互引用是任何一种编程语言的基础能力.对于“模块”这个词在各种编程语言中或许是不同的,但我们可以简单认为一个程序文件是一个模块,文件里包含了类或者方法的定义.对于编译型的语言,比如C#中 ...

  9. python中的引用

    作为一个python初学者,今天被一个python列表和词典引用的问题折磨了很久,但其实了解了缘由也很简单,记录在此备忘. 首先背书python中的引用对象问题: 1. python不允许程序员选择采 ...

随机推荐

  1. Linux命令,附带意思

    1.ls:命令是列出目录内容 2.lsblk:就是列出块设备3.md5sum:就是计算和检验MD5信息签名.4.dd:命令代表了转换和复制文件5.uname显示内核类别, uname -a显示详细信息 ...

  2. Java子类继承父类的执行顺序

    父类的静态代码块(static) 子类的静态代码块(static) 父类的非静态代码块(父类成员初始化) 父类的构造方法 子类的非静态代码块(子类成员初始化) 子类的构造方法

  3. 全面解析UNIX缓冲区溢出 深度防御体系

    首先简要回顾一下缓冲区溢出的攻击大系: ◆栈溢出(stack smashing) 未检查输入缓冲区长度,导致数组越界,覆盖栈中局部变量空间之上的栈桢指针%ebp以及函数返回地址retaddr,当函数返 ...

  4. (九)React Ant Design Pro + .Net5 WebApi:后端环境搭建-IdentityServer4-简单配置

    一.简介 IdentityServer4 是用于 ASP.NET Core 的 OpenID Connect 和 OAuth 2.0 框架,通过中间件的方式集成.JWT(json web token) ...

  5. 阿里云pypi软件包预警

    镜像下载.域名解析.时间同步请点击阿里云开源镜像站 背景 Python库的官方仓库pypi允许开发者自由上传软件包,这会导致某些攻击者利用这点构造恶意包进行供应链攻击,在用户安装包或者引入包时触发恶意 ...

  6. 在 Kubernetes 上快速测试 Citus 分布式 PostgreSQL 集群(分布式表,共置,引用表,列存储)

    准备工作 这里假设,你已经在 k8s 上部署好了基于 Citus 扩展的分布式 PostgreSQL 集群. 查看 Citus 集群(kubectl get po -n citus),1 个 Coor ...

  7. 四旋翼中的PID调节方法 | betaflight固件如何调节PID

    roll横滚,pitch俯仰,yaw航向 一.PID的作用概述 1.P产生响应速度和力度,是I和D的基础 过小响应慢(虽然无震荡) 过大会产生振荡且不断发散 2.D抑制过冲和振荡,抵抗外界的突发干扰, ...

  8. 为什么需要消息系统,mysql 不能满足需求吗?

    1.解耦: 允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束. 2.冗余: 消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据 丢失风险.许多消息队列所采用的& ...

  9. 解释内存中的栈(stack)、堆(heap)和方法区(method area) 的用法?

    通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的 现场保存都使用 JVM 中的栈空间:而通过 new 关键字和构造器创建的对象则放在 堆空间,堆是垃圾收集器管理的主要区域,由于现 ...

  10. 面试问题之C++语言:面向对象的三大特性

    转载于:https://www.cnblogs.com/BEN-LK/p/10720249.html 面向对象的三大特性:封装.继承.多态 封装:就是把客观事物封装成抽象的类,并且类可以把自己的数据和 ...