0.可变与不可变对象,赋值、浅拷贝与深拷贝,函数参数传值
1.第一个题目
a = [1,2]
b = a
print(id(a), id(b))
print(id(a[0]), id(b[0]))
b[0] = 3
print(a, b)
print(id(a[0]), id(b[0]))
print(id(a), id(b))
其结果如下:
可以看到,对一个list来说,a指向的是list的地址,a[0]指向的是值1的地址,b=a,b指向的也是list的地址,在给b[0]赋值的时候,因为1是一个不可变对象,对于指向它的变量的赋值,其实就是指向了一个新的不可变对象,所以地址发生了改变,但是a,b指向的是list的地址,那个地址并没有发生改变
结论就是,常见对象分为可变对象和不可变对象;可变对象有:list,dict,set;不可变对象有:str,int,bool,tuple,float。对可变对象的赋值,两者指向同一个可变对象的地址(引用);对不可变对象的赋值,会生成一个新的不变对象并返回地址
2.第二个题目
import copy
def copy_test():
a = {"x": [1, 2, 3, 4], "u": 123}
b = a
d = copy.deepcopy(a)
e = copy.copy(a)
e["x"][0] = 3
e["u"] = 234
print(id(a) == id(e))
print(a)
copy_test()
结果如下:
深拷贝自然不用说,完全是独立的;赋值之前的题目也有说明;主要考虑下浅拷贝的情况,浅拷贝时,a和e的地址已经不同(引用了不同的对象),但是value还是指向了相同的对象,这时继续需要考虑value是可变对象还是不可变对象来决定后续的赋值变化
因此,对于赋值,浅拷贝,深拷贝来说,区别如下:
2.1 赋值
b = a: 赋值引用,a 和 b 都指向同一个对象
2.2 浅拷贝
b = a.copy(): 浅拷贝, a 和 b 是一个独立的对象,但他们的子对象还是指向统一对象(引用)
2.3 深拷贝
b = copy.deepcopy(a): 深度拷贝, a 和 b 完全拷贝了父对象及其子对象,两者是完全独立的
3.第三个题目
g1 = 1
g2 = []
g3 = 3
print(id(g3))
def f(g3):
print(id(g2),id(g3))
g1 = 2
g3 = 4
print(id(g1),id(g3))
g2.append(1)
f(g3)
print(g1, g2, g3)
结果是:
两个结论:
1.对于不可变对象,函数内部是可以读全局变量的,但是一旦全局变量出现在赋值表达式的左边,就被视为函数内部的新定义的一个局部变量,如果直接使用g1会提示g1未定义;但是对于可变对象,函数内部能拿到全局的可变对象的引用
2.对于函数的参数传递,也是引用传递,对g3赋值后,函数内部的g3就指向别的地址了,全局变量g3并未发生变化;对于可变参数的引用传递,函数内g2内部的值发生变化后,全局的g2也跟着发生了变化
《完》