最新要闻
- 海带怎么炒好吃不老?
- 想要直接降价不可能!英伟达对RTX 40系列显卡促销 送《守望先锋2》游戏
- 国产3A之光《黑神话:悟空》首发试玩资格遭爆炒:最高卖到5000元
- 末代高尔夫油车明年问世
- 南极发现有20只手臂的海怪 头部像颗“大草莓”
- 嘴强王者 男子提供网上代骂服务 一天100元 获利近万元
- 大理白族火把节是哪天 大理火把节氛围感拉满 基本情况讲解
- 以色列精准复刻人体心脏模型:制药业意义重大
- 屏幕堪比手表 小米手环8 Pro续航升级至14天
- 3吨重的一整座电塔飞天刷屏 网友直呼:国家电网版“拖”塔天王
- 吉林省连发两预警 12日至14日将有一次明显降雨天气过程
- 天玑9300将于10月登场:首次全大核架构 性能狙击苹果A17
- 五菱算不算国产车?五菱总经理:唯一一家合资中的国产品牌
- 更方便了!中国、美国间客运航班数量增加一倍 至每周24班
- 国铁回应“高铁票越来越难买”:短途车票敞开出售 会直接挤占长途旅客的票额
- 汽车产业不再被卡脖子!王传福:中国已掌握新能源汽车核心技术和完备的产业链
手机
![顺络电子:董事长部分股权办理股票质押业务](http://www.viltd.com/uploadfile/2022/0610/20220610103218963.jpg)
顺络电子:董事长部分股权办理股票质押业务
![深圳7月二手住宅成交2259套,中介称近期咨询客户开始增加](http://www.viltd.com/uploadfile/2022/0610/20220610103218963.jpg)
深圳7月二手住宅成交2259套,中介称近期咨询客户开始增加
- 顺络电子:董事长部分股权办理股票质押业务
- 深圳7月二手住宅成交2259套,中介称近期咨询客户开始增加
- 最新洪水形势如何?时隔多年为何又见洪水?解答来了!
- 李明俊在调研白龟湖科创新城和环湖路建设工作时强调 勇于担当负责 善于创新突破 着力打造群众满意的放心工程
- 遮天:东荒两大家族登场,庞博成为妖王,妖族公主颜如玉绝美登场
- 京运通: 我司自扩产硅片业务以来,所有单晶炉均为自供
家电
Python 对象拷贝的详细教程
在本篇文章中,会先介绍 Python 中对象的基础概念,之后会提到对象的深浅拷贝以及区别。在阅读后,应该掌握如下的内容:
理解变量、引用和对象的关系
【资料图】
理解 Python 对象中 identity,type 和 value 的概念
什么是 mutable 和 immutable 对象?以及它们和 hashable 的关系
深浅拷贝的过程以及区别
1.变量,引用和对象
变量无类型,它的作用仅仅在某个时候引用了特定的对象而已,具体在内存中就是一个指针,仅仅拥有指向对象的空间大小。
变量和对象的关系在于引用,变量引用对象后,也就对应了赋值的过程。
在 python 中一切皆为对象,具体在内存中表示一块内存空间,每一个对象都会具有 identity,type 和 value 这三个内容。
Identity, 一旦对象被创建后,Identity 的值便不会发生改变。在 Cpython 中,其值体现为内存中保存对象的地址。is 操作符,比较对象是否相等就是通过这个值。通过 id() 函数查看它的整数形式。
Type, 和 Identity 一样,在对象创建后,Type 也不会发生变化。它主要定义了一些可能支持的值和操作(如对列表来说,会有求长度的操作)。通过 type() 函数可以得到对象的类型。
Value,用于表示的某些对象的值。当对象在创建后值可以改变称为 mutable,否则的话被称为 immutable.
举个例子,比如在 C 中,int x = 4 在内存中,是先分配了一个 int 类型的内存空间,然后把 4 放进空间内。
而 Python 中,x = 4 正好相反,是为 4 分配了一块的内存空间,然后用 x 指向它。由于变量可以指向各种类型的对象,因此不需要像 C 一样声明变量。这也就是 Python 被称为动态类型的意义。
并且在 Python 中,变量可以删除,但对象是无法删除的。
2.immutable 和 mutable 对象
immutable 对象拥有一个固定的值,包括 numbers, strings, tuples. 一个新的值被保存时,一个新的对象就会被创建。这些对象在作为常量的 hash 值中有着非常重要的作用,如作为字典的 key 时。
mutable 对象可以改变自身的值,但 id() 并不会发生改变。
当一些对象包含对其他对象的一些引用时,我们称这些对象为 containers, 例如 list, tuple, dictionary 这些都是 containers. 这里需要注意的是,一个 immutable containers 可以包含对 mutable 对象的引用(如在 tuple 中包含一个 list)。 但这个对象仍然称为 immutable 对象,因为 Identity 是不变的。
3.hashable 对象
当一个对象在生命周期内(实现了__hash__()
方法)hash 值不会发生改变,并可以与其他对象进行比较(实现了__eq__()
方法),称之为hashable 对象。
在 Python 内置的 immutable 对象 大多数都是 hashable 对象。immutable containers(tuples, frozenset)在引用的对象都是 hashable 对象时,才是hashable 对象。mutable containers 容器都不是 hashable 对象。用户自定义的类都是 hashable 对象,
4.浅拷贝与深拷贝
在介绍对象的拷贝前,先介绍一下 Python 中的赋值操作,可以让我们更好的了解拷贝的过程。
5.赋值操作
赋值操作的右边是简单表达式:
def normal_operation(): # immutable objects # int a = 10 b = 10 print("----- int") print("id of a:{} , id of b: {}".format(id(a), id(b))) # id of a:1777364320 , id of b: 1777364320 print(a == b) # True print(a is b) # True # str str_a = "123" str_b = "123" print("----- str") print("id of a:{} , id of b: {}".format(id(str_a), id(str_b))) # id of a:1615046978224 , id of b: 1615046978224 print(str_a == str_b) # True print(str_a is str_b) # True # tuple tuple_a = (1, 2, 3) tuple_b = (1, 2, 3) print("----- tuple") print("id of a:{} , id of b: {}".format(id(tuple_a), id(tuple_b))) # id of a:1615047009696 , id of b: 1615047024856 print(tuple_a == tuple_b) # True print(tuple_a is tuple_b) # False # mutable # set set_a = {1, 2, 3} set_b = {1, 2, 3} print("----- set") print("id of a:{} , id of b: {}".format(id(set_a), id(set_b))) # id of a:1615045625000 , id of b: 1615047012872 print(set_a == set_b) # True print(set_a is set_b) # False # list list_a = [1, 2, 3] list_b = [1, 2, 3] print("----- list") print("id of a:{} , id of b: {}".format(id(list_a), id(list_b))) # id of a:1615047017800 , id of b: 1615045537352 print(list_a == list_b) # True print(list_a is list_b) # False # dict dict_a = {"name": "xxx", "age": "123"} dict_b = {"name": "xxx", "age": "123"} print("----- dict") print("id of a:{} , id of b: {}".format(id(dict_a), id(dict_b))) # id of a:1615045521696 , id of b: 1615045522128 print(dict_a == dict_b) # True print(dict_a is dict_b) # False
在 Cpython 中,id() 反映了对象在内存中的地址。可以看到,对于 immutable 对象中的 number 和 string 来说,CPython 本身对其做了一定的优化,在创建相同的内容时,使其 指向了相同的内存地址,从而被复用。
但是,Python 不会对所有 mutable 对象执行此操作,因为实现此功能需要一定的运行时成本。对于在内存中的对象来说,必须首先在内存中搜索对象(搜索意味着时间)。对于 number 和 string 来说,搜索到它们很容易,所以才对其做了这样的优化。
对于其他类型的对象,虽然创建的内容相同,但都在内存中完全创建了一块新的区域。
6. 赋值操作的右边是 Python 中已存在的变量:
def assignment_operation(): # immutable objects # int a = 10 b = a print("----- int") print("id of a:{} , id of b: {}".format(id(a), id(b))) # id of a:1777364320 , id of b: 1777364320 print(a == b) # True print(a is b) # True # str str_a = "123" str_b = str_a print("----- str") print("id of a:{} , id of b: {}".format(id(str_a), id(str_b))) # id of a:2676110142128 , id of b: 2676110142128 print(str_a == str_b) # True print(str_a is str_b) # True # tuple tuple_a = (1, 2, 3) tuple_b = tuple_a print("----- tuple") print("id of a:{} , id of b: {}".format(id(tuple_a), id(tuple_b))) # id of a:2676110191640 , id of b: 2676110191640 print(tuple_a == tuple_b) # True print(tuple_a is tuple_b) # True # mutable # set set_a = {1, 2, 3} set_b = set_a print("----- set") print("id of a:{} , id of b: {}".format(id(set_a), id(set_b))) # id of a:2676108788904 , id of b: 2676108788904 print(set_a == set_b) # True print(set_a is set_b) # True # list list_a = [1, 2, 3] list_b = list_a print("----- list") print("id of a:{} , id of b: {}".format(id(list_a), id(list_b))) # id of a:2676110181704 , id of b: 2676110181704 print(list_a == list_b) # True print(list_a is list_b) # True # dict dict_a = {"name": "xxx", "age": "123"} dict_b = dict_a print("----- dict") print("id of a:{} , id of b: {}".format(id(dict_a), id(dict_b))) # id of a:2676079063328 , id of b: 2676079063328 print(dict_a == dict_b) # True print(dict_a is dict_b) # True
而当赋值操作的右边是已经存在的 Python 对象时,不论是什么类型的对象,都没有在内存中创建新的内容,仅仅是声明了一个新的变量指向之前内存中已经创建的对象,就像提供了一个别名一样。
>>> dict_a = {"1":1}>>> dict_b = dict_a>>> print("id of a:{} , id of b: {}".format(id(dict_a), id(dict_b)))id of a:140355639151936 , id of b: 140355639151936>>> dict_b = {}>>> print("id of a:{} , id of b: {}".format(id(dict_a), id(dict_b)))id of a:140355639151936 , id of b: 140355639922176
由于 dict_b = dict_a操作,让两个变量同时指向了同一块内存区域。自然 id 相等。当对 dict_b 重新赋值时,仅让 b 指向了另外一块内存区域,并不会影响 a 的指向,由于两块内存区域不同,自然id 并不想等。
7.改变赋值后的对象
def assignment_operation_change(): # immutable objects # int a = 10 print("id of a:{}".format(id(a))) # id of a:1994633728 b = a a = a + 10 print("----- int") print("id of a:{} , id of b: {}".format(id(a), id(b))) # id of a:1994634048 , id of b: 1994633728 print(a == b) # False print(a is b) # False # mutable objects # list list_a = [1, 2, 3] list_b = list_a list_a.append(4) print("----- list") print("id of a:{} , id of b: {}".format(id(list_a), id(list_b))) # id of a:2676110181704 , id of b: 2676110181704 print(list_a == list_b) # True print(list_a is list_b) # True
当修改 imutable 对象时,由于其本身不可改变,只能在内存中新申请一块新的空间,用于存储修改后的内容。对应上面 a=20 的操作,这时再判断 a 和 b 时,由于指向了内存的不同位置,所以 a,b不在相等。a 原来指向的内存区域不会被回收,因为现在由 b 指向。可以看到 b 指向的内存地址和 a 之前的指向的内存地址是一致的。
当修改 mutable 对象时,由于都指向相同的内存地址,所以对变量 list_a 修改的操作,也会映射到变量 list_b。
总结一下:
指向 imutable 的不同变量,当其中一个变量被修改时,其他变量不受影响,因为被修改后的变量会指向一个新创建的对象。
指向 mutable 对象的不同变量,当其中一个变量修改这个对象时,会影响到指向这个对象的所有变量。
8.浅拷贝
浅拷贝创建了一个对象,这个对象包含了对被拷贝元素的参考。 所以当使用浅拷贝来复制 conainters 对象时,仅仅拷贝了那些嵌套元素的引用。
def shallow_copy(): # immutable objects # int a = 10 b = copy(a) print("----- int") print("id of a:{} , id of b: {}".format(id(a), id(b))) # id of a:1777364320 , id of b: 1777364320 print(a == b) # True print(a is b) # True # str str_a = "123" str_b = copy(str_a) print("----- str") print("id of a:{} , id of b: {}".format(id(str_a), id(str_b))) # id of a:2676110142128 , id of b: 2676110142128 print(str_a == str_b) # True print(str_a is str_b) # True # tuple tuple_a = (1, 2, 3) # Three methods of shallow copy # tuple_b = tuple_a[:] # tuple_b = tuple(tuple_a) tuple_b = copy(tuple_a) print(id(tuple_b)) print("----- tuple") print("id of a:{} , id of b: {}".format(id(tuple_a), id(tuple_b))) # id of a:2676110191640 , id of b: 2676110191640 print(tuple_a == tuple_b) # True print(tuple_a is tuple_b) # True # mutable # set set_a = {1, 2, 3} # Two methods of shallow copy # set_b = set(set_a) set_b = copy(set_a) print("----- set") print("id of a:{} , id of b: {}".format(id(set_a), id(set_b))) # id of a:2099885540520 , id of b: 2099888490984 print(set_a == set_b) # True print(set_a is set_b) # False # list list_a = [1, 2, 3] # Three methods of shallow copy # list_b = list_a[:] # list_b = list(list_b) list_b = copy(list_a) print("----- list") print("id of a:{} , id of b: {}".format(id(list_a), id(list_b))) # id of a:2099888478280 , id of b: 2099888478472 print(list_a == list_b) # True print(list_a is list_b) # False# Python小白学习交流群:711312441 # dict dict_a = {"name": "xxx", "age": "123"} # Two methods of shallow copy # dict_b = dict(dict_a) dict_b = copy(dict_a) print("----- dict") print("id of a:{} , id of b: {}".format(id(dict_a), id(dict_b))) # id of a:2099855880480 , id of b: 2099886881024 print(dict_a == dict_b) # True print(dict_a is dict_b) # False
这里有一点需要注意,对于 string 和 number 来说,正如上面提到的 Cpython 做了相应的优化,让不同的变量指向了相同的内存地址,进而 id 的值是相等的。
但对于元组这个 immutable 元素来说,执行 浅拷贝时,也不会创建一个内存区域,只是返回一个老元组的引用。
对于其他的 mutable 对象,在浅拷贝后都会创建一个新的内存区域,包含了被拷贝元素的引用。
浅拷贝正如它的名字那样,当拷贝嵌套的 mutable 元素时,就会出现问题:
def shallow_copy_change_value(): # list list_a = [1, 2, 3, [4, 5, 6]] list_b = copy(list_a) list_a[0] = 10 list_a[3].append(7) print("----- list") print("ia:{} ,b: {}".format(list_a, list_b)) print("id of a:{} , id of b: {}".format(id(list_a), id(list_b))) # a:[10, 2, 3, [4, 5, 6, 7]] ,b: [1, 2, 3, [4, 5, 6, 7]] # id of a:1698595158472 , id of b: 1698595159752 print(list_a == list_b) # False print(list_a is list_b) # False
下面是对上面 list 浅拷贝的图解:
执行浅拷贝操作:
72a62de32295d24a41f15aecb2f65eac
在 list_b 执行浅拷贝后,创建一个新的对象,新对象中的 list_a[0] 指向 1.
修改 list_a 操作:
f82e60e92a6c04ab94146965ee4e985e
当执行 list_a[0] = 10 操作时,由于 list_a[0] 本身是 number 类型,会重新创建一块区域,用于保存新的值 10. 而新创建的 list_b[0] 并不会受到影响,还会指向之前的内存区域。
当修改list_a[3] 操作时,由于list_a[3] 在浅拷贝后,新创建的对象中不会 嵌套创建 一个新的 list_a[3] 对象,仅仅是指向了之前的 list_a[3] 对象。所以当修改 list_a[3] 时, list_b[3] 也会收到影响。
9.深拷贝
对于深拷贝操作来说,除了会创建一个新的对象外,会还递归的遍历老对象的中的嵌套元素,并形成新的副本。
def shallow_deepcopy_change_value(): # list list_a = [1, 2, 3, [4, 5, 6]] list_b = deepcopy(list_a) list_a[0] = 10 list_a[3].append(7) print("----- list") print("a:{} ,b: {}".format(list_a, list_b)) print("id of a:{} , id of b: {}".format(id(list_a), id(list_b))) # id of a:2099888478280 , id of b: 2099888478472 print(list_a == list_b) # False print(list_a is list_b) # False
下面是对应图解过程:
执行深拷贝操作:
4a050f660c31f509ecde59eb8b589c38
修改 list_a 操作:
cd944895e4e8cb41278113baa1906af9
这里 list_a 和 list_b 已经是完全的不同的两个对象。
总结
在这篇文章中,主要介绍了 Python 中对象,以及对象的拷贝过程,主要有下面几个重要的内容:
Python 中变量没有类型,仅仅可看做一个指针,通过引用指向对象。变量可以删除,但对象不行。
Python 对象被创建后,会拥有 identity,type 和 value 三个属性。
immutable 和 mutable,主要在于 value 在其生命周期内是否能发生变化。
修改 mutable 对象时,所有指向它的变量都会受到影响。修改 immutable 对象时,指向它的其他变量没有影响。
immutable 的大多数对象都是 hashable,但要考虑 immutable containers 的特殊情况。
浅拷贝会创建一个新的内存区域(对象),但其内部是对原对象内部引用的拷贝,在使用 mutable 对象时,存在一定的风险。
深拷贝不但会创建一个新的内存区域(对象),还会递归的创建原对象的所有嵌套对象,但也带来了一些效率的问题。
关键词:
-
-
-
-
Python 对象拷贝的详细教程
python argparse传入布尔参数不生效的解决方法
海带怎么炒好吃不老?
想要直接降价不可能!英伟达对RTX 40系列显卡促销 送《守望先锋2》游戏
国产3A之光《黑神话:悟空》首发试玩资格遭爆炒:最高卖到5000元
末代高尔夫油车明年问世
南极发现有20只手臂的海怪 头部像颗“大草莓”
嘴强王者 男子提供网上代骂服务 一天100元 获利近万元
大理白族火把节是哪天 大理火把节氛围感拉满 基本情况讲解
以色列精准复刻人体心脏模型:制药业意义重大
屏幕堪比手表 小米手环8 Pro续航升级至14天
3吨重的一整座电塔飞天刷屏 网友直呼:国家电网版“拖”塔天王
吉林省连发两预警 12日至14日将有一次明显降雨天气过程
天玑9300将于10月登场:首次全大核架构 性能狙击苹果A17
五菱算不算国产车?五菱总经理:唯一一家合资中的国产品牌
更方便了!中国、美国间客运航班数量增加一倍 至每周24班
Python教程(7)——一文弄懂Python字符串操作(上)|字符串查找|字符串分割|字符串拼接|字符串替换
国铁回应“高铁票越来越难买”:短途车票敞开出售 会直接挤占长途旅客的票额
汽车产业不再被卡脖子!王传福:中国已掌握新能源汽车核心技术和完备的产业链
中国首颗人工智能卫星发射成功:卫星带了脑子上天
北交所:减免受汛情影响严重企业费用
地球内4亿亿吨黄金,甚至能制作人工合成,为何金价居高不下?
85%肉含量 一口爆汁!森马旗下烤肠大促29.9元3斤大促
杀疯!久违的商战 山姆盒马开打:盒马移山价内涵山姆 任何商品比对方低一元
山东布谷科技直播软件开发WebRTC技术:建立实时通信优质平台
诗经新声-9-《汉广》
北半球三大流星雨之一!英仙座流星雨8月13日迎极大:肉眼可见
苹果又要秒安卓阵营?!A17性能首曝光:比上代提升近50%
21.8万元!领克08订单突破10000台:首发魅族车机+92寸HUD 综合续航1400km
蒙古穗三毛(关于蒙古穗三毛简述)
野火stm32指南者开发板点亮LED
微官网使用方法
中国证监会、香港证监会就推动大宗交易(非自动对盘交易)纳入互联互通机制达成共识
【环球财经】纽约股市三大股指11日涨跌不一
暴雨致车牌丢失怎么办?官方解答:12123手机App即可补办
雪峰科技:8月11日融资买入414万元,融资融券余额3.21亿元
手机相关知识:iphone苹果手机11指纹怎么设置
iPhone 14 Pro电池衰减严重:不到一年只剩86%容量!大批用户截图吐槽
雷军建议大家少刷手机:年轻人不用非得进大厂 偶尔gap一下会走更远
台风卡努在辽宁庄河沿海登陆:成今年首个在东北登陆的台风
“月球-25”号发射成功!俄罗斯:还要挑战载人登月
乒坛大爆冷!16岁日本小将3-1胜亚洲冠军,对手赢过张继科、马龙
选读SQL经典实例笔记23_读后总结与感想兼导读
开背虾的做法大全集窍门(开背虾的家常做法)
达梦数据库随系统开机自动启动脚本
增程是落后技术?销量占插混近1/4 理想、问界等立功
《封神第一部》预测票房超25亿 网友:值得
中年大叔的自拍照 正被网友们拿去当头像 必须要凶悍
Redmi K60至尊版有白色款 员工:边框颜色很特别
想不火都难!将近25万人预约一加Ace 2 Pro
上海楼市政策优化等风来,二手房交易量大增,开发商等待救援
rear空调上是什么意思,大众迈腾rear空调上是什么意思
我和我的祖国诗歌朗诵视频(我和我的祖国诗歌)
小米手环8 Pro来了:这几个大招值得关注
小米MIX Fold 3转轴技惊四座!雷军:手感惊艳
阿里云盘福利:注册多少天送多少GB容量 看看你能领多少?
为何卢伟冰敢说Redmi K60至尊版是性能之王?原因揭开
联发科起飞了!天玑9300脱胎换骨:性能猛如虎
宁波晚报(关于宁波晚报简述)
快速幂算法
大爷高铁上戴耳机刷视频、喝白酒全程安静 网友:让多少年轻人汗颜
疯狂!记者:曼城总价9500万欧求购帕奎塔,西汉姆基础要价1.1亿
element-theme 使用踩坑
【python技巧】文本文件的读写操作
Ant Design Pro项目ProTable怎么实现单元格合并效果
【新华解读】社融数据引发降准预期“拉满” 债市能否迎来新一轮牛市?
一台无人机能替代20名劳动力,江陵县无人机植保为粮食生产提质增效
李楠称中国手机厂商内战内行愚蠢可笑 支持比亚迪主张
科大讯飞净利润同比下降73.54% 回应:钱砸进AI大模型了
荣耀宣布全国范围开通地震预警:烈度高于2.0将弹窗预警
江苏盐城胃肠病医院哪个好
宝马3系遭车主集体投诉:新车就传动轴异响 售后让换轴
搬家神器!长城哈弗猛龙更多信息曝光:后备箱最大空间1404升
单风扇极简风!七彩虹iGame RTX 4060 Ti Mini OC 8GB显卡图赏
男子背140斤冰柜峨眉山顶卖水 网友感慨:6块一瓶真不贵
一起掀桌子!最高下调6万元:11天内十品牌“官降”
“操心”变“暖心” 这个暑期托管班让职工很“省心”
面对AI冲击,技术人才该如何考核?
永别了 Intel迷你机!三款NUC被正式终结
钢铁侠座驾成绝唱!奥迪R8彻底无了:第一代设计师亲手“送别”
五菱推出“移动充电宝”:具备L4级自动驾驶 手机下单就能上门充电
日本北海道附近海域发生6.0级地震:震源深度50公里
杭州女生参加比赛获奖百万:哭着感谢张大大
特效行,剧情人物逻辑跟子供向一样
苹果账号被禁用怎么办?
恭喜又一白鲸开源成员成为 Apache SeaTunnel PMC Member
用python用户注册和短信验证码逻辑实现案例
闻汛而动、暖心安置、合力重建——黑龙江全力打赢防汛救灾“遭遇战”“主动仗”
【财经分析】MSCI中国季度调整新增29只剔除19只 关注对部分个股潜在冲击
4连板塞力医疗:近三年公司主营业务综合毛利率总体呈缓慢下降趋势
一点链接就盗取你的资金?安全大V科普:技术是有的 但别担心
净利润率15%!华为上半年营收出炉:销售收入3109亿 整体稳健
有望搭载自动充气系统 特斯拉皮卡超大“怪物车轮”曝光
挑战ARM!高通恩智浦等5家巨头联手组建新公司 专搞RISC-V
比OLED更完美!苹果未来将在iPhone上使用MicroLED屏
观看曹文轩《草房子》的观后感800字
【金融街发布】证监会同意上海出口集装箱结算运价指数(欧洲航线)期货注册并确定为境内特定品种
【金融街发布】人民银行:前七个月社会融资规模增量累计为22.08万亿元
五烈镇成立困难家庭青少年助学资金会
全球首款ChatGPT音箱来了:搭载双AI 可创作诗歌