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编写的com组件发生R6034错误的原因与解决办法
Apr 01 Python
把MySQL表结构映射为Python中的对象的教程
Apr 07 Python
Python中的元类编程入门指引
Apr 15 Python
Python统计文件中去重后uuid个数的方法
Jul 30 Python
python中map()与zip()操作方法
Feb 27 Python
python中通过预先编译正则表达式提高效率
Sep 25 Python
Python子类继承父类构造函数详解
Feb 19 Python
python实现二维数组的对角线遍历
Mar 02 Python
Python pip替换为阿里源的方法步骤
Jul 02 Python
Python搭建Spark分布式集群环境
Jul 05 Python
Linux下通过python获取本机ip方法示例
Sep 06 Python
Django+boostrap 美化admin后台的操作
Mar 11 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
PHPShop存在多个安全漏洞
2006/10/09 PHP
PHP中使用gettext来支持多语言的方法
2011/05/02 PHP
PHP 实现人民币小写转换成大写的方法及大小写转换函数
2017/11/17 PHP
PHP实现链式操作的三种方法详解
2017/11/16 PHP
JavaScript 处理Iframe自适应高度(同或不同域名下)
2013/03/29 Javascript
完美解决IE低版本不支持call与apply的问题
2013/12/05 Javascript
jQuery实现表格行上移下移和置顶的方法
2015/05/22 Javascript
javascript针对cookie的基本操作实例详解
2015/11/30 Javascript
jQuery实现textarea自动增长宽高的方法
2015/12/18 Javascript
jQuery实现横向带缓冲的水平运动效果(附demo源码下载)
2016/01/29 Javascript
JavaScript入门教程之引用类型
2016/05/04 Javascript
jQuery短信验证倒计时功能实现方法详解
2016/05/25 Javascript
JavaScript实现动态添加Form表单元素的方法示例
2017/08/14 Javascript
H5实现仿flash效果的实现代码
2017/09/29 Javascript
vue-router实现组件间的跳转(参数传递)
2017/11/07 Javascript
JS改变页面颜色源码分享
2018/02/24 Javascript
[03:14]辉夜杯主赛事 12月25日每日之星
2015/12/26 DOTA
极简的Python入门指引
2015/04/01 Python
Python制作简易注册登录系统
2016/12/15 Python
用python记录运行pid,并在需要时kill掉它们的实例
2017/01/16 Python
解决pycharm无法调用pip安装的包问题
2018/05/18 Python
Python实现Event回调机制的方法
2019/02/13 Python
PyQt5实现暗黑风格的计时器
2019/07/29 Python
Python文件操作函数用法实例详解
2019/12/24 Python
python单例设计模式实现解析
2020/01/07 Python
python itsdangerous模块的具体使用方法
2020/02/17 Python
python包的导入方式总结
2021/03/02 Python
CSS3 函数技巧 用css 实现js实现的事情(clac Counters Tooltip)
2017/08/15 HTML / CSS
Pedro官网:新加坡时尚品牌
2019/08/27 全球购物
Lovedrobe官网:英国领先的大码服装品牌
2019/09/19 全球购物
int和Integer有什么区别
2013/05/25 面试题
中学生英语演讲稿
2014/04/26 职场文书
区域销售经理岗位职责
2015/04/02 职场文书
2016会计专业自荐信范文
2016/01/28 职场文书
Golang生成Excel文档的方法步骤
2021/06/09 Golang
SQL Server代理:理解SQL代理错误日志处理方法
2021/06/30 SQL Server