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实现115网盘自动下载的方法
Sep 30 Python
Python使用MYSQLDB实现从数据库中导出XML文件的方法
May 11 Python
Python实现高效求解素数代码实例
Jun 30 Python
对python捕获ctrl+c手工中断程序的两种方法详解
Dec 26 Python
浅谈python 中类属性共享的问题
Jul 02 Python
python单例模式的多种实现方法
Jul 26 Python
浅谈pycharm使用及设置方法
Sep 09 Python
用python的turtle模块实现给女票画个小心心
Nov 23 Python
使用Pandas将inf, nan转化成特定的值
Dec 19 Python
python制作微博图片爬取工具
Jan 16 Python
Python3利用scapy局域网实现自动多线程arp扫描功能
Jan 21 Python
python迷宫问题深度优先遍历实例
Jun 20 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
简单的方法让你的后台登录更加安全(php中加session验证)
2012/08/22 PHP
smarty 缓存控制前的页面静态化原理
2013/03/15 PHP
Yii调试SQL的常用方法
2014/07/09 PHP
php5.4以下版本json不支持不转义内容中文的解决方法
2015/01/13 PHP
thinkPHP框架实现多表查询的方法
2018/06/14 PHP
通过JAVAScript实现页面自适应
2007/01/19 Javascript
jquery nth-child()选择器的简单应用
2010/07/10 Javascript
jQuery.extend 函数的详细用法
2012/06/27 Javascript
JS中的数组的sort方法使用示例
2014/01/22 Javascript
JS中使用Array函数shift和pop创建可忽略参数的例子
2014/05/28 Javascript
Jquery之Bind方法参数传递与接收的三种方法
2014/06/24 Javascript
JavaScript和HTML DOM的区别与联系及Javascript和DOM的关系
2015/11/15 Javascript
vue中渐进过渡效果实现
2016/10/27 Javascript
angular 用拦截器统一处理http请求和响应的方法
2017/06/08 Javascript
详解vue-cli + webpack 多页面实例配置优化方法
2017/07/13 Javascript
WdatePicker.js时间日期插件的使用方法
2017/07/26 Javascript
tsconfig.json配置详解
2019/05/17 Javascript
vue iview多张图片大图预览、缩放翻转
2019/07/13 Javascript
JS使用H5实现图片预览功能
2019/09/30 Javascript
详解JavaScript中的数据类型,以及检测数据类型的方法
2020/09/17 Javascript
[00:39]DOTA2上海特级锦标赛 Liquid战队宣传片
2016/03/04 DOTA
Python实现的简单发送邮件脚本分享
2014/11/07 Python
Python 爬虫之Beautiful Soup模块使用指南
2018/07/05 Python
使用pandas批量处理矢量化字符串的实例讲解
2018/07/10 Python
Python实现字典按key或者value进行排序操作示例【sorted】
2019/05/03 Python
python2 中 unicode 和 str 之间的转换及与python3 str 的区别
2019/07/25 Python
台湾东南旅游社网站:东南旅游
2019/02/11 全球购物
2014年入党积极分子党校培训心得体会
2014/07/08 职场文书
教师党的群众路线教育实践活动学习心得体会
2014/10/30 职场文书
2014年学生会主席工作总结
2014/11/07 职场文书
2014年班主任德育工作总结
2014/12/05 职场文书
颐和园导游词400字
2015/01/30 职场文书
2016年“12.4”法制宣传日活动总结
2016/04/01 职场文书
Nginx 负载均衡是什么以及该如何配置
2021/03/31 Servers
Vue实现tab导航栏并支持左右滑动功能
2021/06/28 Vue.js
Java 数据结构七大排序使用分析
2022/04/02 Java/Android