赋值,浅复制与深复制
在python中,一切皆对象(object),这是说python将所有数据类型例如整数,浮点数,字符串甚至函数都当作对象处理。它们都有自己的地址,数据类型,值(以及方法)。
对象(object)就像一个黑盒,里面装着数据。对象有不同的类型,类型决定了可以对它进行的操作。
我们要明白一点,在python中,对象在赋值之前就已经创建了;是把变量分配给对象,而不是反过来。
x=5
y=[1,2,3]
print(id(x),type(x),x)
print(id(y),type(y),y)
'''
140711537158048 <class 'int'> 5
2232107974208 <class 'list'> [1, 2, 3]
'''
根据对象的更新方式,即对象本身是否可以改变,python数据类型可以分为两类:可变对象(它装着的数据是允许被修改的)和不可变对象(数据是不可修改的常量)。
- 不可变对象有:整数,浮点数,字符串,元组。对象的地址,值不可变。如果变量引用了新的对象,原对象将直接被回收。
- 可变对象有:列表,字典,集合。对象的值可变,地址不变。
x=[1,2,3,4,5]
print(id(x))
x=[1,2,3,4,5]#重新赋值
print(id(x))
x.pop(0)#删
print(id(x))
x.append(6)#增
print(id(x))
x+=[7,8,9]#改
print(id(x))
x=[2,3,4,5,89,75]#重新赋值
print(id(x))
'''
1975804145408
1975804142208不变
1975804142208不变
1975804142208不变
1975804142208不变
1975804145408
'''
如果将一个数据赋给多个变量(不是变量赋给变量)会怎样呢?
- 对不可变对象的变量名进行修改,也就是将一个不可变类型数据赋给多个变量,不会影响到数据在内存中的位置。
- 而将一个可变类型数据赋给多个变量时,会开辟新的内存空间,它们的地址不同。这意味着它们是两个完全不同的对象,包括父对象和嵌套的子对象。
python 中比较两个对象的是否相等,通常有两种形式:
1. `is`:判断两个变量所指对象是否相同,即地址是否相同(同一个对象)。
2. `==`:判断两个变量所指对象的值是否相同。
x=5
y=5
print(id(x),id(y),x is y)#整数,相同
x=[1]
y=[1]
print(id(x),id(y),x is y)#列表,不同
x=(1)
y=(1)
print('truple:',id(x),id(y))#元组,相同
x={1:3}
y={1:3}
print('dictionary:',id(x),id(y))#字典,不同
x=[1]
y=[1]
print('list:',id(x),id(y))#列表,不同
'''
140732431083424 140732431083424 True
1733390548096 1733390544832 False
truple: 140732431083296 140732431083296
dictionary: 1685228416896 1685228416960
list: 1685346823360 1685346820160
'''
在为函数传值时,函数不会对不可变对象产生影响,类似于值传递。但会修改可变对象,类似引用传递。
def fun0(x):
x+=1
x=5
fun0(x)
print(x)
'''
5
'''
def fun1(x):
x[0]=1
x+=[4,5,6]
x=[2,3]
fun1(x)
print(x)
'''
[1, 3, 4, 5, 6]
'''
下面来看变量赋给变量。
1.赋值:python使用=进行赋值。
直接赋值实际上是传递了对象的引用,即地址。对新变量的操作(可变对象)会影响到所有引用该对象的变量。所有变量都是对象的地址引用(无论是数据赋值还是变量赋值)。
python中的变量仅仅是个名字而已,赋值操作不会实际复制值,它只是为对象取个名字。变量就是贴在对象上的标签。
x={1:3,2:4}
y=x
print(id(x),id(y))#相同
print(x is y)#True
print(x==y)#True
x.pop(2)#y同时也被修改
print(y)
'''
2809043152768 2809043152768
True
True
{1:3}
'''
2.浅复制:使用copy模块的copy函数,对象的copy等方法实现。浅复制创建一个新对象,但对象中的子对象不变,仍是引用。
x=[1,2,[1,2,3]]
y=copy.copy(x)#浅复制
print(id(x),id(y))
del x[2][0]
print(id(x))
print(y)
'''
1798056423360 1798056423872
1798056423360
[1, 2, [2, 3]]
'''
可以看出,浅复制仅仅复制第一层,子对象仍是引用。
- 对不可变类型的浅复制:
import copy x=5#元组,浮点数,字符串等是同样的效果 y=copy.copy(x) print(x is y) print(id(x),id(y)) ''' True 140732427085728 140732427085728 '''
- 对可变类型的浅复制:
可以看出,列表与浅复制的对象地址不同,但其子对象即元素地址相同,仍然是引用。修改了嵌套的可变对象数据值,复制的对象也会被改变。import copy x=[1,2,[4,5]] y=copy.copy(x) print(x is y) print(id(x),id(y)) print(x[0] is y[0]) print(x[2] is y[2]) ''' False 2481531232832 2481531233280 True True '''
3.深复制
- 对不可变类型的深复制:
import copy x='libeiqun' y=copy.deepcopy(x) print(id(x),id(y)) print(x is y) ''' 2153490540528 2153490540528 True '''
- 对可变类型的深复制:
1.深复制复制最外层数据,并请求新的内存地址来存放。2.深复制对里面的不可变数据类型直接复制数据和地址,和可变类型的浅复制效果相同。import copy x=[1,2,[4,5]] y=copy.deepcopy(x) print(id(x),id(y))#不同 print(x is y)#False print(id(x[0]),id(y[0]))#相同,因为是不可变类型 print(id(x[2]),id(y[2]))#不同,可变类型 print(x[2] is y[2])#False ''' 2176108699840 2176108700288 False 140732431083296 140732431083296 2176108703040 2176108686912 False ''' x=[[[[1]]]] y=copy.deepcopy(x) print(id(x[0]),id(y[0])) print(id(x[0][0]),id(y[0][0])) print(id(x[0][0][0]),id(y[0][0][0])) ''' 2397555564672 2397555565248 2397555564160 2397555566080 2397555567424 2397555565056 ''' #深复制嵌套可变类型,每一层嵌套都不同。而浅复制则相同
总结:
- 对于不可变对象,只要一建立,系统中只会存在一个,如果没有变量与之关联,系统将会立刻回收;所有的与之关联的变量都指向同一个对象,无论它是不是被嵌套在可变对象里。对其进行深浅拷贝结果相同,地址相同。
- 对于可变对象:浅复制和深复制都会申请一块新的内存,但浅复制只复制最外一层可变数据;深复制复制所有嵌套可变类型数据。不可变类型仍然指向原来的对象。