以下a变量就是对象的引用,1是a指向的内存中存储的数据
a = 1
可变和不可变的区别:能否在不改变内存id的情况下,改变value值。
a = 1
b=[1,2,3]
a=1
print(id(a)) # 140725527819088
a=2
print(id(a)) # 140725527819120
b = [1,2,3]
print(id(b)) # 2155591512904
b.append(4)
print(id(b)) # 2155591512904
如果添加的元素是另一个列表,那么此容器对象中新添加的,其实是另一个列表的容器对象的引用
b = [1,2,3]
print(id(b)) # 2155591512904
b.append([4,5])
print(id(b)) # 2155591512904
a=1
b=a
print(id(a)) # 140725527819088
print(id(b)) # 140725527819088
b=2
print(id(a)) # 140725527819088
print(id(b)) # 140725527819120
由于对象不可变,当变量b改变时,会创建新的对象,指向新的内存,对a变量没有影响。
a = [1,2,3]
b = a
print(id(a)) #2155591539400
print(id(b)) #2155591539400
对于可变对象来说,赋值也是赋的对象的引用,但是是容器对象的引用。
b[2] = 4
print(a[2]) # 4
当b的值发生变化时,是容器对象中的某个元素指向的对象发生改变,因此会影响到a.
b=a[:]
或
b=a.copy()
例
a=[1,2,3,[4,5]]
b=a[:]
print(id(a)) # 2155591539272
print(id(b)) # 2155591540424
当浅拷贝时,会创建新的容器对象,但这些容器对象指向相同的元素对象。
a[0] = 7
print(b[0]) # 1
a[3][1]=6
print(b[3][1]) # 6
当a想要指向另一个不可变对象,对b没有影响;当指向的可变对象某元素改变时,会影响b。
import copy
a=[1,2,3,[4,5]]
b=copy.deepcopy(a)
print(id(a)) #2155591633288
print(id(b)) #2155591513096
a[0] = 7
print(b[0]) # 1
a[3][1]=6
print(b[3][1]) # 5
彻底复制,a,b再也不会引用相同的容器对象。分别指向的容器对象数值更改不会再互相影响。
a=1
print(id(a)) #140725527819088
a += 1
print(id(a)) # 140725527819120
a = a+1
print(id(a)) #140725527819152
三次地址都不同,因为对于不可变对象 += 和=+ 调用的都是 __add__函数;要改变值,变量一定会引用其他的对象。
b=[1,2,3]
print(id(b)) # 2155591281416
b += [4]
print(id(b)) # 2155591281416
b = b + [4]
print(id(b)) # 2155589911432
对于可变对象,+=时相当于调用__iadd__函数,相当于原地修改,不改变引用对象地址id。
而调用= + 时调用__add__函数,相当于浅复制.
函数中实参赋给形参值时,都是传递的对象的引用。但由于两种对象的可变和不可变性质,看起来效果不同。
def func(x):
x += 1
return x
a=3
func(a)
print(a) # 3
当a传入函数后,a 的对象(3) 创建了新的引用x,当变量x发生改变时,指向了新的对象;实参a并没有受到影响。
def f1(x):
x += [1]
a=[1,2]
f1(a)
print(a) # [1,2,1]
a传入函数的是容器对象的引用,所以如果对变量x做类似append和+=的操作时,会改变实参a。
a=[1,2]
f1(a[:])
print(a) # [1,2]
a传入参数的是容器对象的拷贝,不论在函数中对变量x做什么操作,都不会影响实参a。但如果a的元素中有列表,浅拷贝时有影响,深拷贝没有。
def f2(x):
x = x + [1]
a = [1,2]
f1(a)
print(a) # [1,2]
f1(a[:])
print(a) # [1,2]
由于函数f2中对变量x做了 =+的操作,相当于做了浅拷贝,所以对a没有影响