这是我参与更文挑战的第 14 天,活动详情查看: 更文挑战
1.参数简介
在上一张节,我们学习函数,涉及到相关的形参和实参
那我们本期就来详细学习一下参数传递的过程是怎么样的?
1.1 什么是参数
参数分为形参和实参。
形参名叫形式参数,相当于X,用于占位用
实参名叫实际参数,传入具体的值给函数进行处理的参数
- 形参
- 定义函数时在圆括号里定义的参数(相当于X,占位)
- 只能在函数里使用
- 实参
- 调用函数时传入的具体参数
- 通过函数后面的括号传递给函数,让函数处理的值
- 形参与实参的关系
- 形参用于函数定义,实参用于函数调用
- 形参在函数起到占位;实参在函数调用后传入给函数处理
- 实参涉及到参数传递,形参主要有5种类型
1.3 参数分类
| 序号 | 名字 | 作用 |
|---|---|---|
| 1 | 必备参数 | 调用函数时以正确的顺序传入函数 |
| 2 | 关键字参数 | 调用函数时通过名字传入参数值 |
| 3 | 默认参数 | 指定形参默认值 |
| 4 | 不定长参数 | 允许形参前加入*号,调用函数可以接收多个值,当成元组传入函数 |
| 5 | 逆向参数收集 | 调用函数时拆分列表/元组/字典等对象传入给函数参数 |
本次,先大概了解参数类型,我们下期详细学习。
2.参数传递内存分析
函数的参数传递本质上是:从实参到形参的赋值操作。
Python中,一切都是对象。所有的赋值操作都是“引用赋值”。所以,Python中参数传递都是“引用传递”,不是“值传递”。
具体分两类:
- 对可变对象,进行写操作,直接用于源对象本身
- 对不可变对象,进行写操作,会产生一个新的对象空间,并用于新的值填充这块空间。
好啦,我们再来复习一下,可变对象和不可变对象。
-
可变对象
字典、列表、集合、自定义的对象等
-
不可变对象
数字、字符串、元组、函数等
2.1 传递可变对象
传递参数是可变对象,实际传递的还是对象的引用。在
函数体中不创建新的对象拷贝,而是可以直接修改传递的对象。
接下来我们具体来看看传递可变对象的过程
# 定义一个列表
a = [1, 2]
# 定义一个函数,列表后面添加元素
def listappend(b):
print("b:",b)
print("b:",id(b)) # b 和 a 是同一个对象
b.append(30)
# 调用listappend()函数,传入实参变量a
listappend(a)
print("a:",a)
print("a:",id(a))
- 定义实参[1,2] 和 listappend函数对象
在栈内存中分别创建变量a 和 listappend栈区
在listappend栈区创建变量b
- 调用函数listappend()函数
实参对象[1,2]传入给函数listappend()形参b。
实参变量a 和 b 地址是一样
由于传入的实参a是列表数据类型,属于可变对象,可变的对象特点是地址不变,值可以改变。
因此,仍然在变量a对象[1,2]里末尾进行添加对象30的引用
最后调用print(),把结果打印在电脑屏幕上
2.2 传递不可变对象
传递参数是不可变对象(如:num,str,tuple等),实际传递的还是对象的引用。
在赋值操作时,由于不可变对象无法修改,系统系统会创建新创建一个对象。
同样,我们还是来看个栗子
# 定义a,Sum函数
a = 10
def Sum(b):
print("b:",b)
print("b:", id(b)) # a 和 b 传递进来的是对象10的地址
b = b + 100 # 由于对象10是不可变对象,因此会创建新的地址空间
print("b:",b) # b变量已经重新指向新对象
print("b:", id(b))
# 调用函数
Sum(a)
print("a:",a)
print("a:", id(a))
- 定义实参为10,函数Sum()
在栈内存中分别创建变量a 和 Sum栈区
在Sum栈区创建变量b
- 调用函数Sum()
把变量a 的对象10传入给Sum()形参b
由于对象10是不可变对象,因此改变的话,要重新要重新创建新地址
变量b重新指向新的地址(计算结果)
2.3 传递不可变对象含可变对象
具上一章节深浅拷贝的学习,了解深浅拷贝原理。
传递参数是不可变对象,在写操作是,会创建一个新对象的拷贝。这个拷贝的过程就是浅拷贝(引用拷贝),而不是深拷贝。
举个列子
# 定义一个不可变对象
a = (1,[3, 4])
def test(b):
print("b:", id(b))
print("b[1]:", id(b[1]))
b[1][1] = 100
print("b:",b)
print("b:", id(b))
print("b[1]:", id(b[1]))
#调用函数
test(a)
print("a:",a)
print("a:", id(a))
- 定义变量a和test()函数
在栈区创建变a 指向堆区对象(1,[2,3])
在栈区创建test栈区,并且在test栈区创建变量b
- 调用函数
变量a对象传入给函数test变量b
变量a和变量指向同一个地址(1,[2,3])
由于对象a[1]是可变对象,因此无需新创建空间
a[1][1]的位置上更概引用,指向对象100位置
总结
经过上面的介绍,我们对形参和实参有了认识,并且对参数传递进行深刻的学习。
接下来,我们后续会继续学习,前面了解到参数类型及用法。
好啦,以上是本期内容,欢迎大佬们评论区指正,下次见~