Python中不允许程序员选择传值还是传引用。函数参数传递采用的是“传对象引用”。简而言之,若参数是可变类型[字典、列表、集合] 即不能被哈希的对象,就能修改对象的原始值;若参数是不可变类型[数值、字符、元组]即能被哈希的对象,就不能修改对象原始值。
例:可变类型的引用
p1 = [1,2,3]
def list_1(p):
p = p+[1]
print(p)
print(id(p))
list_1(p1)
print(p1)
print(id(p1))
>>>[1, 2, 3, 1]
>>>2060407209792
>>>[1, 2, 3]
>>>2060405764736
p2 = [1,2,3]
def list_2(p):
p +=[1]
print(p)
print(id(p))
list_2(p2)
print(p2)
print(id(p2))
>>>[1, 2, 3, 1]
>>>2060407209792
>>>[1, 2, 3, 1]
>>>2060407209792
上述两个函数,之所以输出结果不同,是因为“=”操作符是在内存中创建一个新变量p保存赋值结果,即所谓“传值”,通过观察他们的ID不同,我们也能理解。而“+=”操作符则是直接对传入的p2修改,即所谓“传引用”,他们的ID也相同,符合推断。
例:不可变类型的引用
a = 100
def list_1(p):
p = p+100
print(p)
print(id(p))
list_1(a)
print(a)
print(id(a))
>>>200
>>>140709999032288
>>>100
>>>140709999029088
b = 1000
def list_2(p):
p +=100
print(p)
print(id(p))
list_2(b)
print(b)
print(id(b))
>>>1100
>>>3033967515952
>>>1000
>>>3033966466800
由于数值是不可变类型,所以无论操作符是“=”或者“+=”,都是采用”传值“ 的方式进行参数传参,观察他们的ID也可以发现,四个ID都各不相同。
直接赋值”=“:就是对象的引用。
浅拷贝:拷贝父对象,不会拷贝对象的内部的子对象
深拷贝:使用copy模块的 deepcopy 方法,完全拷贝父对象及其子对象
#直接赋值 列表实例
list3 = [123, 456]
print(id(list3))
list5 = list3
print(list5)
print(id(list5))
list3[0]=1
print(list3)
print(id(list3))
print(list5)
print(id(list5))
>>>2164292645056
>>>[123, 456]
>>>2164292645056
>>>[1, 456]
>>>2164292645056
>>>[1, 456]
>>>2164292645056
#浅拷贝 字典实例
a = {1:[1,2,3]}
b = a.copy()
print(a,b)
print(id(a),id(b))
a[1].append(4)
print(a,b)
print(id(a[1]),id(b[1]))
>>>{1: [1, 2, 3]} {1: [1, 2, 3]}
>>>2539185110080 2539185110144
>>>{1: [1, 2, 3, 4]} {1: [1, 2, 3, 4]}
>>>2539185173120 2539185173120
#深拷贝 字典实例
import copy
a = {1:[1,2,3]}
c = copy.deepcopy(a)
print(a,c)
print(id(a),id(c))
a[1].append(5)
print(a,c)
print(id(a[1]),id(c[1]))
>>>{1: [1, 2, 3]} {1: [1, 2, 3]}
>>>2258846940160 2258852622272
>>>{1: [1, 2, 3, 5]} {1: [1, 2, 3]}
>>>2258853762240 2258853762432
1.观察直接赋值实例可得,list3和list5其实在内存中都指向同一地址,即指向同一个对象
2.观察浅拷贝实例可得,浅拷贝下的a和b是一个独立个体,在内存中有不同的地址,但他们的子对象,仍然指向统一对象(引用)
3.观察深拷贝实例可得,深拷贝下的a和b是一个独立个体,在内存中有不同的地址,同时他们的子对象,也是独立个体,在内存中拥有不同的地址。
引用的关键是分清函数传参时,参数对象到底是可变类型还是不可变类型。可变类型传参效果类似”传引用“;不可变类型传参效果类似”传值“
直接赋值、浅拷贝和深拷贝关键在于弄清楚他们的子对象是独立个体还是统一引用,通过观察他们的ID可以比较清楚的理解它们的不同。