Python的对象传递与Copy函数使用详解


Posted in Python onDecember 26, 2019

1、对象引用的传值或者传引用

Python中的对象赋值实际上是简单的对象引用。也就是说,当你创建一个对象,然后把它赋值给另一个变量的时候,Python并没有拷贝这个对象,而是拷贝了这个对象的引用。这种方式相当于值传递和引用传递的一种综合。如果函数收到的是一个可变对象(比如字典或者列表)的引用,就能修改对象的原始值--相当于通过“引用传递”来赋值。如果函数收到的是一个不可变变量(比如数字、字符串或者元祖)的引用,就不能直接修改原始对象--相当于通过“值传递”来赋值。

先看一个数字传递的例子:

>>> def test(a):
...     print id(a)
...     a = a + 1
...     print id(a)
...     return a
...
>>> b =19
>>> id(b)
38896272
>>> c = test(b)
38896272
38896260
>>> id(b)
38896272
>>> b
19

id函数可以获得对象的内存地址.

很明显从上面例子可以看出,将b变量作为参数传递给了test函数,传递了b的一个引用,把b的地址传递过去了,所以在函数内获取的变量a的地址跟变量b的地址是一样的,但是在函数内,对a进行赋值运算,a的值从19变成了20,实际上19和20所占的内存空间都还是存在的,赋值运算后,a指向20所在的内存。而b仍然指向19所在的内存,所以后面打印b,其值还是19.

另外,关于整数变量的id,所有在[-5,256]范围内的整数,python是提前分配好空间放在数组里初始化好的,所以两个变量如果是相同的小整数,对象都是最开始初始化的那一个,所以两个变量的id是一样的。

所有在[-5,256]范围外的整数的话,每次都会新建一个的,所以id会改变

>>> a = 256
>>> id(a)
43340980
>>> b = 256
>>> id(b)
43340980   # a和b的id相同
>>> a = 257
>>> id(a)
44621040
>>> b = 257
>>> id(b)
44620908   # a和b的id不同
>>> a = -5
>>> id(a)
43338160
>>> b = -5
>>> id(b)
43338160
>>> a = -6
>>> id(a)
44621184
>>> b = -6
>>> id(b)
44621112

再看一个列表传递的例子:

>>> def test(a):
...   print id(a)
...   a[0] = 100
...   print id(a)
...   return a
...
>>> b = [7,8,9,10]
>>> id(b)
46408088
>>> c = test(b)
46408088
46408088
>>> id(b)
46408088
>>> b
[100, 8, 9, 10]

从上面例子可以看出,将b变量作为参数传递给了test函数,传递了b的一个引用,把b的地址传递过去了,所以在函数内获取的变量a的地址跟变量b的地址是一样的,但是在函数内,对a进行赋值运算,a[0]的值从7变成了100,但是a的id并没有发生变化,还是和变量b的地址是一样的,所以后面打印b,b[0]的值也从7变成了100.

2、关于可变变量和不可变变量:

这里的可变不可变,是指内存中的那块内容(value)是否可以被改变

不可变变量:

number: int, float, str, 元组。--指它的部分(比如element,attribute不能改变)不能改变;并不是整体不可变。另外,Python所有变量皆对象。int也是一个对象。

>>> a = 10000
>>> id(a)
46573412
>>> a = 10000000
>>> id(a)
46573460   #数字变量重新赋值后,id发生了变化
>>> s = 'abc'
>>> s[1] = d   #字符串变量中的某一个元素不能进行改变
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> id(s)
39103328
>>> s = 'ttt'
>>> id(s)    #字符串变量进行重新赋值后,id发生了变化
46425368

从上面的例子中可以看出,数字变量、字符变量在重新赋值后,id都会发生变化,这是因为不可变变量的赋值是通过在内存中新申请一块区域,把新的值存储到该区域,然后改变不可变变量的引用,指向新的内存区域,从而改变了不可变变量的值。

可变变量

class, class instance;列表,dict,

例1.可变变量中元素的赋值

>>> list = [1,2,3]
>>> id(list)
45486568
>>> for i in list:
...   print id(i)
40207208
40207196
40207184
>>> list[0] = 0
>>> id(list)
45486568   # 变量的id并没有发生改变
>>> for i in list:
...   print id(i)
40207220    # 该元素的id发生了改变
40207196
40207184
例2.可变变量的赋值
>>> list = [1,2,3]
>>> id(list)
43783392
>>> list =[2,3,5] 
>>> id(list)  # 该变量的id发生了改变
44454296

从上面的例子可以看出,列表中的元素重新赋值,整个列表的id不会发生改变,但是该元素的id会发生该生。因为列表中存储的其实是对各个元素的引用,所以对该元素赋值的结果就是元素的引用发生了改变。

总之,无论是可变变量还是不可变变量,只要对整个变量进行赋值,Python都在内存中新申请一块区域,把新的值存储到该区域,然后改变不可变变量的引用,指向新的内存区域;如果可变变量中的元素进行赋值,支队导致该元素的变化,不会导致父对象的变化。

3、 深拷贝 Vs 浅拷贝

copy.copy() 浅拷贝

copy.deepcopy() 深拷贝

浅拷贝是新创建了一个跟原对象一样的类型,但是其内容是对原对象元素的引用。这个拷贝的对象本身是新的,但内容不是。如果原对象的元素包含不是基本数据结构,而是list、dict或者对象的话,那么原对象或者拷贝对象改变list、dict或者对象里面的内容的话,会导致二者同时发生改变。

深拷贝则是对原对象的完全拷贝,包含对象里面的子对象的拷贝,因此拷贝对象和原对象二者是完全独立,任何一方的改变对另外一方都不会产生任何的影响。

>>> import copy
>>> list = [1, 2, [3, 4]]
>>> copy_list = copy.copy(list)
>>> deepcopy_list = copy.deepcopy(list)
>>>
>>> id(list)
44454296
>>> id(copy_list)
44515736
>>> id(deepcopy_list)
44455736
>>>
>>> for k in list:
...   print id(k)
43338088 43338076 44430120
>>> for k in copy_list:
...   print id(k)
43338088 43338076 44430120  # copy对象的内容和原对象完全一样
>>> for k in deepcopy_list:
...   print id(k)
43338088 43338076 44457456  # deepcopy对象的内容和原对象有区别:列表元素的id不一样;数字元素id一样,原因是所有相同数字的变量的引用都是一样的。
>>> 
>>> list[2][0] = 30
>>> list
[1, 2, [30, 4]]
>>> copy_list
[1, 2, [30, 4]]   # 原对象的子对象中的元素发生改变后,会导致copy对象发生同样的改变  
>>> deepcopy_list
[1, 2, [3, 4]]    #原对象的子对象中的元素发生改变后,不会导致deepcopy对象发生同样的改变

以上这篇Python的对象传递与Copy函数使用详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
实例讲解Python中的私有属性
Aug 21 Python
Python搭建HTTP服务器和FTP服务器
Mar 09 Python
pandas中Timestamp类用法详解
Dec 11 Python
利用python脚本如何简化jar操作命令
Feb 24 Python
基于Python的PIL库学习详解
May 10 Python
pandas实现将dataframe满足某一条件的值选出
Jun 12 Python
Python输出指定字符串的方法
Feb 06 Python
PyQt5+python3+pycharm开发环境配置教程
Mar 24 Python
查看keras各种网络结构各层的名字方式
Jun 11 Python
分布式全文检索引擎ElasticSearch原理及使用实例
Nov 14 Python
用Python爬虫破解滑动验证码的案例解析
May 06 Python
python控制台打印log输出重复的解决方法
May 14 Python
Python pandas库中的isnull()详解
Dec 26 #Python
python dataframe NaN处理方式
Dec 26 #Python
python实现大战外星人小游戏实例代码
Dec 26 #Python
Python数据存储之 h5py详解
Dec 26 #Python
Python 使用 prettytable 库打印表格美化输出功能
Dec 26 #Python
Python实现图片识别加翻译功能
Dec 26 #Python
opencv resize图片为正方形尺寸的实现方法
Dec 26 #Python
You might like
html中select语句读取mysql表中内容
2006/10/09 PHP
php 无限极分类
2008/03/27 PHP
介绍一些PHP判断变量的函数
2012/04/24 PHP
PHP @ at 记号的作用示例介绍
2014/10/10 PHP
PHP制作登录异常ip检测功能的实例代码
2016/11/16 PHP
PHP实现的日历功能示例
2018/09/01 PHP
基于JQuery的动态删除Table表格的行和列的代码
2011/05/12 Javascript
在jquery中处理带有命名空间的XML数据
2011/06/13 Javascript
javascript跑马灯悬停放大效果实现代码
2012/12/12 Javascript
javascript相等运算符与等同运算符详细介绍
2013/11/09 Javascript
查找Oracle高消耗语句的方法
2014/03/22 Javascript
事件委托与阻止冒泡阻止其父元素事件触发
2014/09/02 Javascript
告诉你什么是javascript的回调函数
2014/09/04 Javascript
js实现上一页下一页的效果【附代码】
2016/03/10 Javascript
微信小程序 本地存储及登录页面处理实例详解
2017/01/11 Javascript
js图片放大镜效果实现方法详解
2020/10/28 Javascript
Jquery中attr与prop的区别详解
2017/05/27 jQuery
使用Bootstrap和Vue实现用户信息的编辑删除功能
2017/10/25 Javascript
webpack写jquery插件的环境配置
2017/12/21 jQuery
vue写h5页面的方法总结
2019/02/12 Javascript
vue使用screenfull插件实现全屏功能
2020/09/17 Javascript
[02:44]重置世界,颠覆未来——DOTA2 7.23版本震撼上线
2019/12/01 DOTA
pygame学习笔记(6):完成一个简单的游戏
2015/04/15 Python
Python实现模拟分割大文件及多线程处理的方法
2017/10/10 Python
Python实现发送与接收邮件的方法详解
2018/03/28 Python
使用Python爬虫库requests发送表单数据和JSON数据
2020/01/25 Python
Pycharm安装python库的方法
2020/11/24 Python
美国电视购物HSN官网:HSN
2016/09/07 全球购物
Kangol帽子官网:坎戈尔袋鼠
2018/09/26 全球购物
什么叫做SQL注入,如何防止
2016/10/04 面试题
高三自我鉴定怎么写
2013/10/19 职场文书
《绿色蝈蝈》教学反思
2014/03/02 职场文书
工作评语大全
2014/04/26 职场文书
死亡证明书样本说明
2014/10/18 职场文书
Python使用scapy模块发包收包
2021/05/07 Python
解析python中的jsonpath 提取器
2022/01/18 Python