流畅的python--深拷贝,浅拷贝

时间:2021-09-20 19:54:00

说到python中的对象引用问题,还得看看is和==的区别:

>>> a = [1,2,3,4]
>>> b = [1,2,3,4]
>>> a == b
True
>>> a is b
False


  可以看见a和b中的数值都是一样的,但是==与is的结果却不是一样的,这是因为python中==比较的是a和b的数值相等,is比较对象的标识是否相等。所以在python中我们经常会使用==来比较对象的数值时候相等, 判断对象绑定的值时候是None, 最好使用is。这里面有一个坑,一些新手经常犯的错误,空字符串,空列表,空字典,他们的都等于False,但是他们都不是(不等于)None,所以在判断一个字符串,列表,字典是否为空时, 不要用None来做比较,因为这些变量无论是从对象标识,数值都与None不相等。

  我们平常是用的对象复制一般都是浅拷贝。copy模块为我们提供了copy(浅拷贝),deepcopy(深拷贝)函数。

什么是浅拷贝:

  浅拷贝就是将拷贝的对象引用拷贝一份,拷贝对象指向的是被拷贝对象的数值,简单的说就是,在原有数值上面再添加的一份引用。

>>> a =1
>>> b =a
>>> id(a)
4297636352
>>> id(b)
4297636352

什么是深拷贝:

  深拷贝就是拷贝对象对被拷贝对象数值上复制一份,然后新建一个对象,这个新的对象数值,对象标识都是和被拷贝对象相等的,

>>> import copy
>>> a = [1,2,3]
>>> b = copy.deepcopy(a)
>>> b
[1,2,3]
>>> a
[1,2,3]
>>> a == b
True
>>> a is b
True
>>> id(a)
4297636352
>>> id(b)
4297636352
>>> a.append(0)
>>> a
[1,2,3,0]
>>> b
[1,2,3]
>>> b.append(9)
>>> b
[1, 2, 3, 9]
>>> a
[1, 2, 3, 0]

  基于这种现象,所以我们应该特别注意函数在使用可变参数
  作为默认参数,如不注意就会出现下面这种情况:

>>> def a(x = []):
... x.append(0)
... print(x)
...
>>> a()
[0]
>>> a()
[0, 0]
>>> a()
[0, 0, 0]

  为了避免这种情况我们应该避免使用可变对象作为函数默认参数:

>>> def a(x = None):
... if x is None:
... x = []
... else:
... x.append(0)
... print(x)
...
>>> a()
[]
>>> a()
[]

同时创建类初始化传参也是使用浅拷贝来传递的,这样就会出现这种情况:

>>> class A:
... def __init__(self, name):
... self.name = name
... def printf(self):
... print(self.name)
...
>>> x = [1,2,3,4]
>>> a = A(x)
>>> a.printf()
[1, 2, 3, 4]
>>> x.append(5)
>>> a.printf()
[1, 2, 3, 4, 5]

在传入的参数改变的时候,类里面的变量的值页跟着改变了,这种情况时最懒发现的。