python中拷贝对象的区别

 一、赋值、引用

在python中赋值语句总是建立对象的引用值,而不是复制对象。因此,python变量更像是指针,而不是数据存储区域

这点和大多数语音类似吧,比如C++、Java等

 1、先看个例子:

values=[0,1,2]
values[1]=values
print(values)    # [0, [...], 2]

 预想应该是:[0,[0,1,2],2],但结果却为何要赋值无限次?

可以说 Python 没有赋值,只有引用。你这样相当于创建了一个引用自身的结构,所以导致了无限循环。为了理解这个问题,有个基本概念需要搞清楚。
Python 没有「变量」,我们平时所说的变量其实只是「标签」,是引用。
执行:values=[0,1,2]的时候,python做的事情是首先创建一个列表对象[0,1,2],然后给它贴上名为values的标签。如果随后执行values=[3,4,5]
的话,python做的事情是创建另一个列表对象[3,4,5],然后把刚才那张名为values的标签从前面的[0,1,2]对象上撕下来,重新贴到[3,4,5]这个对象上。

至始至终,并没有一个叫做 values 的列表对象容器存在,Python 也没有把任何对象的值复制进 values 去。过程如图所示:

执行:values[1]=values的时候,python做的事情则是把values这个标签所引用的列表对象的第二个元素指向values所引用的列表对象本身。执行完毕后,values

 标签还是指向原来那个对象,只不过那个对象的结构发生了变化,从之前的列表[0,1,2]变成了[0,?,2],而这个?则是指向那个对象本身的

一个引用。如图所示:

要达到你所需要的效果,即得到 [0, [0, 1, 2], 2] 这个对象,你不能直接将 values[1] 指向 values 引用的对象本身,而是需要吧 [0, 1, 2] 这个对象「复制」一遍,得到一个新对象,再将 values[1] 指向这个复制后的对象。Python 里面复制对象的操作因对象类型而异,复制列表 values 的操作是

values[:]   # 生成对象的拷贝或者是复制序列,不再是引用和共享变量,但此法只能顶层复制

 所以你需要执行:values[1]=values[:]

Python 做的事情是,先 dereference 得到 values 所指向的对象 [0, 1, 2],然后执行 [0, 1, 2][:] 复制操作得到一个新的对象,内容也是 [0, 1, 2],然后将 values 所指向的列表对象的第二个元素指向这个复制二来的列表对象,最终 values 指向的对象是 [0, [0, 1, 2], 2]。过程如图所示:

往更深处说,values[:] 复制操作是所谓的「浅复制」(shallow copy),当列表对象有嵌套的时候也会产生出乎意料的错误,比如

a=[0,[1,2],3]
b=a[:]
a[0]=8
a[1][1]=9
print(a)    # [8, [1, 9], 3]
print(b)    # [0, [1, 9], 3]

b 的第二个元素也被改变了。想想是为什么?不明白的话看下图

正确的复制嵌套元素的方法是进行「深复制」(deep copy),方法是

import copy

a = [0, [1, 2], 3]
b = copy.deepcopy(a)
a[0] = 8
a[1][1] = 9

2、引用vs拷贝

(1)没有限制条件的分片表达式(L[:])能够复制序列,但此法只能浅层复制。

(2)字典 copy 方法,D.copy() 能够复制字典,但此法只能浅层复制

(3)有些内置函数,例如 list,能够生成拷贝 list(L)

(4)copy 标准库模块能够生成完整拷贝:deepcopy 本质上是递归 copy

(5)对于不可变对象和可变对象来说,浅复制都是复制的引用,只是因为复制不变对象和复制不变对象的引用是等效的(因为对象不可变,当改变时会新建对象重新赋值)。所以看起来浅复制只复制不可变对象(整数,实数,字符串等),对于可变对象,浅复制其实是创建了一个对于该对象的引用,也就是说只是给同一个对象贴上了另一个标签而已。

3、增强赋值以及共享引用

x = x + y,x 出现两次,必须执行两次,性能不好,合并必须新建对象 x,然后复制两个列表合并

属于复制/拷贝

x += y,x 只出现一次,也只会计算一次,性能好,不生成新对象,只在内存块末尾增加元素。

当 x、y 为list时, += 会自动调用 extend 方法进行合并运算,in-place change。

属于共享引用

 

二、深拷贝deepcopy与浅拷贝copy

python中的对象之间赋值时是按引用传送的,如果需要拷贝对象,需要使用标准库中的copy模块

1、copy.copy 浅拷贝,只拷贝父对象,不会拷贝对象的内部的子对象。(子对象(数组)修改,也会修改)

2、copy.deepcopy 深拷贝,拷贝对象及其子对象(原始对象)

import copy
a=[1,2,[3,4],{'a':1}]   # 原始对象
b=a     # 赋值,传对象的引用
c=copy.copy(a)  # 对象拷贝,浅拷贝
d=copy.deepcopy(a)  # 对象拷贝,深拷贝
e=a[:]  # 能复制序列,浅拷贝

a.append('add1')  # 修改对象a
a[2].append('add2')  # 修改对象a中的[3,4]数组对象
a[3]='666'
print('a:',a)
print('b:',b)
print('c:',c)
print('d:',d)
print('e:',e)

"""
执行结果:
a: [1, 2, [3, 4, 'add2'], '666', 'add1']
b: [1, 2, [3, 4, 'add2'], '666', 'add1']
c: [1, 2, [3, 4, 'add2'], {'a': 1}]
d: [1, 2, [3, 4], {'a': 1}]
e: [1, 2, [3, 4, 'add2'], {'a': 1}]
解释:copy.copy 浅拷贝 只拷贝父对象,不会拷贝对象的内部的子对象。子对象(数组)修改,也会修改
copy.deepcopy 深拷贝 拷贝对象及其子对象(原始对象)
"""

 

三、深入理解python变量作用域及其陷阱

1、可变对象&不可变对象

在Python中,对象分为两种:可变对象和不可变对象,不可变对象包括int,float,long,str,tuple等,可变对象包括list,set,dict等。需要注意的是:这里说的不可变指的是值的不可变。对于不可变类型的变量,如果要更改变量,则会创建一个新值,把变量绑定到新值上,而旧值如果没有被引用就等待垃圾回收。另外,不可变的类型可以计算hash值,作为字典的key。可变类型数据对对象操作的时候,不需要再在其他地方申请内存,只需要在此对象后面连续申请(+/-)即可,也就是它的内存地址会保持不变,但区域会变长或者变短。

a='hello'
print(id(a))    # 1991608735200
a='python'
print(id(a))    # 1991608735368

# 重新赋值之后,变量a的内存地址已经变了
# 'hello'是str类型,不可变,所以赋值操作知识重新创建了str 'python'对象,然后将变量a指向了它


l1=[1,2,3]
print(id(l1))   # 2262493958280
l1.append(4)
print(id(l1))   # 2262493958280

# list重新赋值之后,变量l1的内存地址并未改变
# [1, 2, 3]是可变的,append操作只是改变了其value,变量l1指向没有变

  

2、函数值传递

def func_int(a):
    a+=4

def func_list(l1):
    l1[0]=4

t=0
func_int(t)
print(t)    # 0

t_list=[1,2,3]
func_list(t_list)
print(t_list)   # [4, 2, 3]

对于上面的输出,不少Python初学者都比较疑惑:第一个例子看起来像是传值,而第二个例子确实传引用。其实,解释这个问题也非常容易,主要是因为可变对象和不可变对象的原因:对于可变对象,对象的操作不会重建对象,而对于不可变对象,每一次操作就重建新的对象。

    在函数参数传递的时候,Python其实就是把参数里传入的变量对应的对象的引用依次赋值给对应的函数内部变量。参照上面的例子来说明更容易理解,func_int中的局部变量"a"其实是全部变量"t"所指向对象的另一个引用,由于整数对象是不可变的,所以当func_int对变量"a"进行修改的时候,实际上是将局部变量"a"指向到了整数对象"1"。所以很明显,func_list修改的是一个可变的对象,局部变量"a"和全局变量"t_list"指向的还是同一个对象。

 

3、陷阱:使用可变的默认参数

我多次见到过如下的代码:
def foo(a, b, c=[]):
# append to c
# do some more stuff
永远不要使用可变的默认参数,可以使用如下的代码代替:
def foo(a, b, c=None):
    if c is None:
        c = []
    # append to c
    # do some more stuff
‍‍与其解释这个问题是什么,不如展示下使用可变默认参数的影响:‍‍
In[2]: def foo(a, b, c=[]):
...        c.append(a)
...        c.append(b)
...        print(c)
...
In[3]: foo(1, 1)
[1, 1]
In[4]: foo(1, 1)
[1, 1, 1, 1]
In[5]: foo(1, 1)
[1, 1, 1, 1, 1, 1]
同一个变量c在函数调用的每一次都被反复引用。这可能有一些意想不到的后果。

参考:http://www.cnblogs.com/jiangzhaowei/p/5740913.html

 

转载于:https://www.cnblogs.com/shenbuer/p/7977323.html

Ubuntu 24.04 部署 EMQX 5.8 开源版MQTT服务器:从零搭建到实战应用 本文详细指导如何在Ubuntu 24.04系统上部署EMQX 5.8开源版MQTT服务器。内容涵盖从环境准备、安装Erlang、下载安装包到启动服务的完整步骤,并提供了验证服务、配置网络防火墙以及通过WebSocket工具进行基础消息收发的实战方法,帮助用户快速搭建稳定、免费的物联网消息中间件。 阅读详情

相关推荐

HC32 CAN通信

qqa

zhaopenglihui的博客 5430

Python里面如何拷贝一个对象?(赋值,浅拷贝,深拷贝的区别)

答:赋值(=),就是创建了对象的一个新的引用,修改其中任意一个变量都会影响到另一个。 浅拷贝:创建一个新的对象,但它包含的是对原始对象中包含项的引用(如果用引用的方式修改其中一个对象,另外一个也会修改改变){1,完全切片方法;2,工厂函数,如list();3,copy模块的copy()函数} 深拷贝:创建一个新的对象,并且递归的复制它所包含的对象(修改其中一个,另外一个不会改变)...

weixin_30501857的博客 275

ST电机库实战:从状态机到稳健启动流程的深度解析

本文深度解析了ST电机库中状态机的核心作用与稳健启动流程。状态机作为电机控制的“交通指挥官”,通过IDLE、CHARGE_BOOT_CAP、ALIGNMENT等一系列状态的严格跳转,确保了永磁同步电机(PMSM)和直流无刷电机(BLDC)的安全启动与可靠运行,是避免硬件损坏和实现平稳控制的关键。

weixin_29161785的博客 224

python引用、浅拷贝、深拷贝

在python中,对象的赋值往往是通过“=”进行的。但是由于“=”右边的类型方式不一样,所以产生变量虽然值相同,但是在内存中的地址值不同,可能会造成内存空间的浪费。如题所示,对象赋值中存在三种类型情况的赋值 引用赋值 引用赋值l2=l1 ,首先创建个变量l2,然后在创建一条引用,把这条引用指向l1的地址值。然后l1和l2共同使用这一块地址值。 当l1进行变化后,l2也跟着变化 l1=...

weixin_43979988的博客 631

STM32F103ZE驱动PMW3901光流模块,从SPI配置到数据读取的完整避坑指南

本文详细解析了STM32F103ZE驱动PMW3901光流模块的全过程,从SPI配置、硬件连接到数据读取与处理,提供了完整的避坑指南。重点介绍了SPI参数配置、初始化序列优化、运动数据滤波等关键技巧,帮助开发者快速实现精准的光流检测。

weixin_42631411的博客 513

外骨骼机器人(二):自适应振荡器

振荡器是一种数学工具,可以用于实时跟踪、预测类周期信号的频率、相位、幅值等,而助行外骨骼机器人需要自动适应人体的行走频率、幅度,两者相结合,就能使助行外骨骼机器人拥有更舒适、更自然的行走步态。 ......

行者无疆的博客 4262

Python如何拷贝一个对象

还可以使用copyreg.pickle模块的dumps和loads方法来自定义指定类型对象的拷贝行为。deepcopy可以通过memo字典来保存已经拷贝过的对象,从而避免刚才所说的自引用递归问题。浅拷贝通常只复制对象本身,而深拷贝不仅会复制对象,还会递归的复制对象所关联的对象。copy.copy()浅拷贝,copy.deepcopy()深拷贝。deepcopy函数的本质就是对象的一次序列化和一次反回序列化。

qq_46130027的博客 448

基于STM32LXXX的模数转换芯片ADC(CMS24AD2001)驱动C程序设计

/ 电压 = (原始值 / 2^23) * Vref (注意:24位有符号数范围为 -2^23 到 2^23-1)//---------------- 硬件端口映射 (请根据实际电路修改) ----------------ADC_SCLK_HIGH();- PGA 放大倍数可选:1、2、4、8、16、32、64、128、256;//---------------- 宏定义操作 -------------------//---------------- 函数声明 -------------------

myron6688的博客 159

python中如何进行拷贝的_python中的对象拷贝

python中。进行函数參数传递或者返回值时,假设是一般的变量,会拷贝传递。假设是列表或字典则是引用传递。那python怎样对列表和字典进行拷贝传递呢:标准库的copy模块提供了两个方法:copy和deepcopy方法。1. copy.copy 浅拷贝 仅仅拷贝父对象,不会拷贝对象的内部的子对象。2. copy.deepcopy 深拷贝 拷贝对象及其子对象见下例:import copya = [1...

weixin_39876592的博客 350

《YOLO11改进有效涨点》 专栏目录 | 目前已有100+篇内容,内含各种Head检测头、损失函数Loss、Backbone、Neck等创新点改进【持续更新】

YOLO11改进专栏目录,YOLO11改进目录

kay_545 1万+

C++和OpenGL实现3D游戏编程【连载7】——文字和汉字的显示(附源码)

上一节我们讨论了纹理在二维平面内不规则图形贴图的相关基础操作,本节我们开始了解游戏里文字以及汉字的显示方法。本节课我们将从基本的ASCII字符显示,拓展到中文字符的显示,最后再讲到纹理字符的显示,并对各种文字显示方法的优缺点和使用场景进行分析,这节课将使用到显示列表、兼容视图、屏幕图形转纹理等操作。

zhooyu的博客 1497

Python基础:对象的深拷贝和浅拷贝的区别

Python基础:对象的深拷贝和浅拷贝的区别1 变量与对象2 不可变对象与可变对象3 直接赋值4 浅拷贝5 深拷贝参考文献 1 变量与对象 对象:内存中存储数据的实体,有明确的类型。在Python中,一切都是对象,函数也是对象。 变量:指向对象的指针,对对象的引用。作为弱类型语言,Python中变量是没有类型的。 2 不可变对象与可变对象 不可变对象:对象对应内存中的值不会变,因此如果指向该对象的变量被改变了,会重新开辟一片内存,变量指向这个新的内存,包括int、float、str、tuple。 可变

紫芝的博客 259

Flink 运维监控与指标采集实战(Prometheus + Grafana 全流程)

Flink运维监控在实时计算场景中至关重要,因为Flink作业需要7×24小时稳定运行,对性能、资源、故障感知和状态变化的实时监控是保障系统稳定的关键。Flink自带的Metrics系统提供了基本的监控能力,但为了更全面的监控,Prometheus+Grafana成为最主流的解决方案。通过配置PrometheusReporter和Grafana仪表盘,可以实现对Flink作业的全面监控,包括CPU、内存、吞吐量、Checkpoint状态、KafkaSink性能等关键指标。此外,自定义指标采集和报警机制的配置

晴天彩虹雨的博客 1368

Python进阶,对象赋值、浅拷贝、深拷贝的区别

我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。深拷贝创建一个完全独立的对象,包括对象内部的所有嵌套对象。观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

m0_59236602的博客 991

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Python中如何拷贝一个对象?(赋值、深拷贝、浅拷贝的区别)

一、赋值、引用 在python中赋值语句总是建立对象的引用值,而不是复制对象。因此,python变量更像是指针,而不是数据存储区域 这点和大多数语音类似吧,比如C++、Java等  1、先看个例子: 1 2 3 values=[0,1,2] values[1]=values print(values)    # [0, [....

一米阳光-的博客 3万+

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Python中如何拷贝一个对象?(赋值、浅拷贝、深拷贝的区别)

赋值,浅拷贝,深拷贝不同! 非常值得参考:https://www.cnblogs.com/shenbuer/p/7977323.html 1.赋值(引用)和复制 Python 没有赋值,只有引用。Python 没有「变量」,我们平时所说的变量其实只是「标签」,是引用。 values=[0,1,2] values[1]=values print(values) # [0, [.....

zz2230633069的博客 625

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

python深拷贝和浅拷贝的区别不可变对象_Python里面如何拷贝一个对象?(赋值,浅拷贝,深拷贝的区别)?...

欢迎关注公众号:数据科学中文社区我从“可变对象的原处修改”这里引入,这是一个值得注意的问题。上一小节我们谈到,赋值操作总是存储对象的引用,而不是这些对象的拷贝。由于在这个过程中赋值操作会产生相同对象的多个引用,因此我们需要意识到“可变对象”在这里可能存在的问题:在原处修改可变对象是可能会影响程序中其他引用该对象的变量。如果你不想看到这种情景,则你需要明确的拷贝一个对象,而不是简单赋值。X = [1...

weixin_32821643的博客 146

python 对象拷贝, 值相同,同一对象的区别

python 对象拷贝 小例奉上:            可以看出,赋值号“=”是引用,即a 和 b 是指向一个对象。     如何实现对象的拷贝呢,python 有 copy模块。 用法:import copy               对象2 = copy.copy(对象1) 例子:              问题:                  可

pfm685757的专栏 1314

python的对象引用、深拷贝、浅拷贝的区别

1、对于python对象的引用,即把一个对象直接赋值给另外一个对象; 代码如下: b对象和a对象完全一样;创建一个新的变量,传递a的引用,内存地址,b和a完全一样,这就是引用传递的含义吧。即两个变量指向一个内存地址,参考大佬的图解如下: 2、浅拷贝 b变量会对a对象中的地址复制一份,但是不会拷贝内部的元素; 可以看出c变量的地址和a的不同,但是内部元素存储的地址完全相同,对于浅拷贝来说,只会保存原有对象的内存地址,但是不会保存内部的元素;(即如果原有对象内部元素的内存地址改变,在浅拷贝后的变量中是看不出

cy_believ的博客 501

python 浅拷贝与深拷贝、对象赋值的区别

转自  : http://www.cnblogs.com/wilber2013/p/4645353.htmlPython中,对象的赋值,拷贝(深/浅拷贝)之间是有差异的,如果使用的时候不注意,就可能产生意外的结果。下面本文就通过简单的例子介绍一下这些概念之间的差别。对象赋值直接看一段代码:will = ["Will", 28, ["Python", "C#", "JavaScript"]] wil...

kllkzl3的博客 214

Python--对象赋值、浅拷贝以及深拷贝之间的区别

对象赋值、浅拷贝以及深拷贝之间的区别 1.1 对象赋值: >>> list1 = ["hello", 2018, ["Python", "Java", "C#"]] >>> id(list1) 2656094454216 >>> list2 = list1 >>&g

代码的世界 199

Python - 对象赋值、浅拷贝、深拷贝的区别

前言 Python 中不存在值传递,一切传递的都是对象的引用,也可以认为是传址 这里会讲三个概念:对象赋值、浅拷贝、深拷贝 名词解释 变量:存储对象的引用 对象:会被分配一块内存,存储实际的数据,比如字符串、数字、列表 引用:变量指向对象,可以理解为指针 实际的一种应用场景 有一个变量 a,存储了一个值 此时想用另一个变量 b 暂时存储变量 a 的值,以便后续使用 然后继续修改变量 ...

qq_33801641的博客 231

浅拷贝直接赋值_Python里面如何拷贝一个对象?(赋值,浅拷贝,深拷贝的区别)?...

欢迎各位小哥哥小姐姐阅读本的文章,对大家学习有帮助,请点赞加关注哦!!!!!!!!!!您的点赞和关注将是我持续更新的动力呢.^v^有不懂的问题可以私聊我哦!Python里面如何拷贝一个对象?(赋值,浅拷贝,深拷贝的区别)赋值操作总是存储对象的引用,而不是这些对象的拷贝。由于在这个过程中赋值操作会产生相同对象的多个引用,因此我们需要意识到“可变对象”在这里可能存在的问题:在原处修改可变对象是可能会影...

weixin_39992072的博客 176

详解python列表等对象的赋值和复制(浅拷贝copy()及深拷贝deepcopy()的使用区别与示例)

python中针对列表等对象、子对象等情况,如果区分等号赋值、浅拷贝、深拷贝等功能。详解列表赋值、copy()函数、deepcopy()函数

大龙虾的技术成长专栏 4592
上一篇: 判断python对象是否可调用的三种方式及其区别
下一篇: python的with语句
diaoding3046
博客等级 码龄11年 2粉丝 0原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值