参数的传递
函数的参数传递本质上就是:从实参到形参的赋值操作。Python中“一切皆对象”,所有的赋值操作皆是“引用赋值”,所以,Python中参数的传递都是“引用传递”,不是“值传递”,具体操作可分为两类:
- 对“可变对象”进行“写操作”,直接作用于原对象本身。
- 对“不可变对象”进行“写操作”,会产生一个新的“对象空间”,并用新的值填充这块空间(起到其他语言的“值传递”效果,但不是“值传递”)。
可变对象有:
字典、列表、集合、自定义的对象等
不可变对象有:
数字、字符串、元组、function等
传递可变对象的引用
传递参数是可变对象(如:字典、列表、自定义的其他可变对象等),实际传递的还是对象的引用。在函数体中不创建新的对象拷贝,而是可以直接修改所传递的对象。
例子:
b=[10,20]
def one(n):
print("n",id(n)) #b和n是同一个对象,n是局部变量
n.append(55) #由于n是可变对象,不创建对象拷贝,直接修改这个对象
one(b)
print("b",id(b))
print(b)
执行返回:
>>>n 1542470710600
b 1542470710600
[10, 20, 55]
注意(个人理解)(图里缺局部栈帧那一块)
- 上面函数的内存分析。栈为变量,堆为内存。变量b指向内存对象(内存地址为id(b)也就是打印出来1542470710600),内存对象有包含两个对象(10和20)有可以理解内存对象的索引1和2分别指向10和20。最终指向b。
- 调用函数的把对象b传递给n,因为n是局部变量,将形成栈帧,栈帧引用指向b的内存对象,所以n加55,在b的内存对象里面也是增加一个对象指向30的对象,同时对象30地址给了内存对象里面。栈帧消失,结果不变
传递不可变对象的引用
传递参数是不可变对象(例如:int、float、字符串、元组、布尔值等),实际传递的还是对象的引用,早“赋值操作”时,由于不可变对象无法修改,系统会创建一个新的对象。
操作:参数传递,传递不可变对象的引用
a=100
def one(n):
print("n",id(n)) #传递进来的是a对象地址
n=n+100 #由于a是不可变对象,因此创建新的对象n
print("n1",id(n)) #n已经变成新的对象
print(n)
one(a)
print("A",id(a))
返回值:
>>>n 1655292064
n1 1655295264
200
A 1655292064
显然,通过id值可以看到n和a一开始是一个对象,给n赋值后,n是新的对象。
参数传递,不可变对象含可变对象
传递不可变对象时用的是浅拷贝
传递参数是不可变对象(例如:int,float,字符串、元组、布尔值),实际传递的还是对象的引用。但在“写操作”时,会创建一个新的对象拷贝。这个拷贝使用的是“浅拷贝”,不是深拷贝。
a=(10,20,[2,3]) #元组是不可变对象,列表是可变对象
print("a:",id(a))
def test_1(b):
print('b:',id(b))
b[2][0]=666
print(b)
print('b_:',id(b))
test_1(a)
print(a)
返回:
>>>a: 2128142246680
b: 2128142246680
(10, 20, [666, 3])
b_: 2128142246680
(10, 20, [666, 3])
注:传递不可变对象时,不可变对象里面包含的子对象是可变的,则方法内修改这个可变对象,源对象已发生了变化。