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

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函数使用详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • python中copy()与deepcopy()的区别小结

    前言 copy()与deepcopy()之间的区分必须要涉及到python对于数据的存储方式. 深复制被复制对象完全再复制一遍作为独立的新个体单独存在.所以改变原有被复制对象不会对已经复制出来的新对象产生影响. 浅复制并不会产生一个独立的对象单独存在,他只是将原有的数据块打上一个新标签,所以当其中一个标签被改变的时候,数据块就会发生变化,另一个标签也会随之改变. import copy origin = [1, 2, [3, 4]] #origin 里边有三个元素:1, 2,[3, 4] cop

  • python深copy和浅copy区别对比解析

    这篇文章主要介绍了python深copy和浅copy区别对比解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 首先先看一段代码 import copy a = [1, 2, 3, [4]] b = a c = a.copy() d = copy.deepcopy(a) e = copy.copy(a) a.append(4) a[3].append(5) print('a值为 {} 地址为 {}'.format(a, id(a))) prin

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

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

  • Python中常用的高阶函数实例详解

    前言 高阶函数指的是能接收函数作为参数的函数或类:python中有一些内置的高阶函数,在某些场合使用可以提高代码的效率. lambda 当在使用一些函数的时候,我们不需要显式定义函数名称,直接传入lambda匿名函数即可.lambda匿名函数通常和其他函数搭配使用. 比如可以直接使用如下的lambda表达式计算当x=3时,y = x * 3 + 5的函数值. In [1]: (lambda x: x * 3 + 5)(3) Out[1]: 14 map map函数将一个函数和序列/迭代器(可以传

  • Python基础学习之时间转换函数用法详解

    本文实例讲述了Python基础学习之时间转换函数用法.分享给大家供大家参考,具体如下: 前言 python的时间格式分为多种,几种格式之间的转换方法时常是我们遇到的而且是经常忘记的点,python不像php,时间字符串和datetime是一起的,只需要strtotime和date函数就可以相互转化.虽然网上已经有很多python时间转换的文章,但是由于作者本人经常做海外业务,需要各种时区之间的转换,所以这篇文章会对按时区转换各种时间格式做一个总结. 转换方法图示(图片转自网络): 一.字符串转时

  • Python range、enumerate和zip函数用法详解

    前言 range函数可创建一个整数列表. 如果需要知道当前元素在列表中的索引,推荐用enumerate代替range. zip函数用于同时遍历多个迭代器. 一.range 函数 range函数可创建一个整数列表,一般用在 for 循环中. 语法: range([start,] stop[, step]) 参数: start: 计数从 start 开始.默认是从 0 开始.例如range(5)等价于range(0,5); stop: 计数到 stop 结束,但不包括 stop.例如:range(0

  • Python列表常用函数使用详解

    目录 介绍  append() extend() insert() pop() remove() 介绍  append() 语法 list.append( element ) 参数 element:任何类型的元素 列表「末尾」添加元素 name_list = ['zhangsan', 'lisi', 'wangwu'] name_list.append('zhaoliu') print(name_list) 输出: ['zhangsan', 'lisi', 'wangwu', 'zhaoliu'

  • Python入门之三角函数sin()函数实例详解

    描述 sin()返回的x弧度的正弦值. 语法 以下是sin()方法的语法: importmath math.sin(x) 注意:sin()是不能直接访问的,需要导入math模块,然后通过math静态对象调用该方法. 参数 x--一个数值. 返回值 返回的x弧度的正弦值,数值在-1到1之间. 实例 以下展示了使用sin()方法的实例: #!/usr/bin/python import math print "sin(3) : ", math.sin(3) print "sin(

  • Python入门之三角函数tan()函数实例详解

    描述 tan() 返回x弧度的正弦值. 语法 以下是 tan() 方法的语法: import math math.tan(x) 注意:tan()是不能直接访问的,需要导入 math 模块,然后通过 math 静态对象调用该方法. 参数 x -- 一个数值. 返回值 返回x弧度的正弦值,数值在 -1 到 1 之间. 实例 以下展示了使用 tan() 方法的实例: #!/usr/bin/python import math print "tan(3) : ", math.tan(3) pr

  • Python的Lambda函数用法详解

    在Python中有两种函数,一种是def定义的函数,另一种是lambda函数,也就是大家常说的匿名函数.今天我就和大家聊聊lambda函数,在Python编程中,大家习惯将其称为表达式. 1.为什么要用lambda函数? 先举一个例子:将一个列表里的每个元素都平方. 先用def来定义函数,代码如下 def sq(x): return x*x map(sq,[y for y in range(10)]) 再用lambda函数来编写代码 map(lambda x: x*x,[y for y in r

  • Python中flatten( )函数及函数用法详解

    flatten()函数用法 flatten是numpy.ndarray.flatten的一个函数,即返回一个一维数组. flatten只能适用于numpy对象,即array或者mat,普通的list列表不适用!. a.flatten():a是个数组,a.flatten()就是把a降到一维,默认是按行的方向降 . a.flatten().A:a是个矩阵,降维后还是个矩阵,矩阵.A(等效于矩阵.getA())变成了数组.具体看下面的例子: 1.用于array(数组)对象 >>> from n

  • python groupby 函数 as_index详解

    在官方网站中对as_index有以下介绍: as_index : boolean, default True For aggregated output, return object with group labels as the index. Only relevant for DataFrame input. as_index=False is effectively "SQL-style" grouped output 翻译过来就是说as_index 的默认值为True, 对于

随机推荐