zl程序教程

您现在的位置是:首页 >  Python

当前栏目

python--拷贝

2023-02-26 09:49:33 时间

对于传参数还是传引用还是这个问题,在上学C语言的时候就被烦过一段时间。那在python中,参数传递是传参还是传引用呢?拷贝为什么还分浅拷贝和深拷贝呢?区别是什么呢?本文主要来介绍python中的拷贝。

数据类型

在了解拷贝之前,我们先了解下python中的数据类型。

python中的内置数据类型,分为可变和不可变两种。

可变:列表,字典,集合

不可变:整数,浮点数,字符串,元组

比较好理解,列表是可以进行增删改的,而整数1就是1,1不能改为2。

nums = [1,2,3,4,5]
id(nums)
nums.append(6)
id(nums)

nums的值变量,但是内存地址没变

a=1
id(a)
a=2
id(a)

a的值变了,执行不同的整数,内存地址也就变了

def add_str(_str):
    _str += "def"
    return _str
    
def add_list(_list, val):
    _list.append(val)
    return _list

str = "abc"
add_str(str)
==> abc

nums = [1,2,3]
add_list(nums, 4)
==>[1,2,3,4]

以上例子不可变类型做为参数传入,变量没有被改变;可变类型传入后,变量被改变了。

原始是因为在python中参数传递传入的是变量所指对象的引用,由于字符串是不可变变量,函数内对字符串进行了修改就需要新开一个内存地址,保存新的字符串,所以不会对传入变量进行修改。

而列表是可变类型,变量进行了修改,但是内存地址还是同一个地址,所以传入的变量也发生了变化。

浅拷贝

import copy
a = [1,2,3,4]
b = a.copy()
b.append(5)
id(a)
id(b)
==>a=[1,2,3,4]
==>b=[1,2,3,4,5]

浅拷贝是创建一个新的容器,对于对象中的元素,还是使用原始元素的引用。

上面的例子,b是一个新的容器,所以可变列表修改后,不会影响a。

import copy
a = [1,[2],3,4]
b = a.copy()
b[1]=0
id(a)
id(b)
==>a=[1,[0],3,4]
==>b=[1,[0],3,4]

以上情况,由于a[1]和b[1]是同一个引用,我们修改b[1],还是会影响a[1]的,这种情况就需要使用深拷贝。

深拷贝

深拷贝和浅拷贝一样会创建一个新的容器,并且对于对象中的元素也会重新生成一个新的对象。

a=[1,[2,3],4,5]
b = copy.deepcopy(a)
b[1].append(0)
==>
a=[1,[2,3],4,5]
b=[1,[2,3,0],4,5]

赋值

赋值和深拷贝浅拷贝不一样,它是并没有创建任何新的容器的。我们可以看看传递参数是肤质还是拷贝。

import copy
def fun(b):
    print("arg id = ", id(b))
    b[0]=2

a=[1,2,3,4]
a_copy=a.copy()
print("a id = ",id(a))
print("a_copy id = ",id(a_copy))
fun(a)
print(a)
==>
a id =  4387526848
a_copy id =  4387526688
arg id =  4387526848
[2, 2, 3, 4]

通过以上打印可以看出,传参数是一个赋值过程,并没有创建新的容器,只有拷贝才会创建新的容器,所以这种情况下一定要注意,可变变量在函数体中的修改是会影响变量的。