函数形参根据传入的方式分为:
1. 按照形参位置顺序传参
2. 按照形参名关键字传参
3. 解包传参
4. 实参解包
在python中存在可变类型与不可变类型,使用可变类型传参时尤其要小心,所以首先了解一下函数传参的引用值特性(赋值传参)
引用赋值传参
所谓赋值传参就是传递实参时,如果传入一个实参变量,那么相当于赋值语句: 形参名
=
变量实参
由于Python的赋值是引用赋值, 如果传入的变量是可变类型,那么就需要注意可能发生修改原变量的问题
什么是引用赋值
# 传给函数的参数变量是对变量自身的引用
a = 1000
print(id(a))
def test_id(arg):
print(id(arg))
# 传入实参a, 可以观察到id(arg)与id(a)为同一值
test_id(a)
可变类型变量作为实参的影响
函数内部对形参进行成员修改,会影响外部的变量
例1:使用列表类型值作为实参
# 可变类型变量作为实参
# 例1,列表类型作为实参
lists = [1, 2, 3]
print(id(lists))
# 函数内部对arg进行了append操作
def test_id(arg):
print(id(arg))
arg.append(1)
print(id(arg))
# 实参调用,这时候可以看到lists也发生了变化
test_id(lists)
print(id(lists))
print(lists)
dicts = {"name": "luxp", "age": 28}
print(id(dicts))
def test_id(arg):
print(id(arg))
arg['name'] = 'wxp'
# 实参调用
test_id(dicts)
print(id(dicts))
print(dicts)
顺序传参
按照形参参数顺序进行传参,不能缺少,错位
# 在myfun函数中定义了两个形参number1与number2
def myfun(number1, number2):
print(number1 - number2)
# 缺少形参调用就会报错
myfun(10)
# 报错:TypeError: myfun() missing 1 required positional argument: 'number2'
# 提示你缺少number2参数
# 正确做法, 定义几个形参就输入几个形参
myfun(10, 20)
设置形参默认值
如果形参的大部分的情况是某个特定值,那么就可以使用一个默认值,
使用默认值的形参,在传参值为默认值时就可以省略这一项参数
# 计算物体的重力需要物体质量与所在经纬度的重力加速度
def get_gravity(m, g):
return m * g
# 计算10kg物体重力
get_gravity(10, 9.8)
# 大多时候,某一个地区的重力加速度是固定的
# 这时候就可以设定一个加速度g默认值
def get_gravity(m, g=9.8):
return m * g
# g为默认值时调用,只需要输入m值
get_gravity(10)
# 如果需要计算不同g值,只要传入不同的g值就可以
get_gravity(10, 9.82)
get_gravity(10, 9.78)
设置形参默认值注意事项
1. 默认值位置
具有默认值的形参需要在所有形参的最右边!!!
由于具有形参默认值形参可以省略,而在按照位置顺序传参时,参数是根据形参从左到右顺序传递,
所以:有默认值的形参必须在所有无默认值形参的最右边
# 具有默认值的形参必须在最右边
def myfun(arg1, arg2, arg3, arg4=0, arg5=0):
for i in range(1, 6):
print(f"arg{i}=", eval(f"arg{i}"))
# arg4, arg5都是用默认值
myfun(1, 2, 3)
# arg4传入实参4,arg5使用默认值
myfun(1, 2, 3, 4)
# arg4传入实参4, arg5传入实参5
myfun(1, 2, 3, 4, 5)
2. 默认值类型
默认值用字符串,数字,元组等不可变类型,是不会出意外问题的
可变类型作为默认值时是很容易出现bug的
如果使用列表、字典等可变类型作为默认值,就要注意了,结果可能超出你的预料
def myfun(arg1, arg2=[]):
arg2.append(arg1)
print(arg2)
# arg2默认值变成了上一次的执行结果
myfun(1)
# 输出 [1]
myfun(2)
# 输出 [1,2]
myfun(3)
# 输出 [1,2,3]
# 从上面的结果可以看出,每执行一次,args2的实际值就会变化一次,所以定义默认值一定要小心类型的选择
关键字传参
在函数形参数量比较少的情况下,按照顺序传参不会有太大问题,但是如果形参数量很多,可以想象到很容易把参数顺序搞错,或者参数遗漏,这个时候,就可以使用关键字参数
传递实参
# 关键字传参就是用形参名+实参对的形式进行传参,由于指定了形参与实参对应关系,所以不用遵守顺序
def myfun(number1, number2):
print(number1 - number2)
# 按照关键字传参
myfun(number1=10, number2=100)
# 关键字传参可以不用遵守形参定义顺序
myfun(number2=10, number1=100)
函数参数收集
在项目实践中,有时候函数的参数未必确定,或者有很多的参数,这时候使用*args,**kwargs定义形参会更加方便
1. *args形式的位置形参
# 函数定义了一个*args形参
# 在调用时,传给函数的所有顺序实参都可以通过args获得
def myfun(*args):
print(args)
# 可以传入任意数量的参数
myfun(1,2,3,4)
在上面的代码中,函数具有一个形参*args
,但是在调用的时候传入了四个实参myfun(1,2,3,4)
*args会被初始化成一个元组,将所有顺序实参作为元组的一个成员,所以在函数内部,可以通过args的元组操作来获得对应的实参
这段代码执行后,输出是:(1,2,3,4)一个元组
2. **kwargs形式的关键字形参
# 函数定义了一个**kwargs参数
# 所有通过关键字实参传入的参数都可以通过kwargs获取
def myfun(**kwargs):
print(kwargs)
# 可以传入任意数量的关键字参数
myfun(name="luxp", sex="男", age="18")
在上面这段代码中,函数具有一个形参**kwargs, 但是在调用的时候传入了三个实参myfun(name="luxp", sex="男", age="18")
**kwargs
会被初始化为一个字典,将每个关键字参数作为字典的一个成员,所以在函数内部,可以通过kwargs的字典操作来获得对应实参
这段代码执行后,输出是:{'name': 'luxp', 'sex': '男', 'age': '18'}一个字典
3. *args与**kwargs可以同时使用
注意事项
- 定义时*args需要在**kwargs左侧
- 调用时,顺序实参也需要在关键字参数左侧
def myfun(*args,**kwargs):
print(kwargs)
print(args)
4. *args与**kwargs可以与其他形式参数一起混用
注意事项
*args,**kwargs必须在其他形参最右侧
# *args与**kwargs可以与其他形式参数一起混用
def myfun(name, sex, kw="0", *args, **kwargs):
print(name)
print(sex)
print(kw)
print(args)
print(kwargs)
# 调用1
myfun("luxp", "女")
# 调用2
myfun("luxp", "女", 10)
# 调用3
myfun("luxp", "女", 10, "学生", "单身狗", job="程序yuan")
调用时,请注意下面代码的参数变化与输出变化
调用: myfun("luxp", "女") 输出: luxp 女 0 () {}
调用:myfun("luxp", "女", 10) 输出: luxp 女 10 () {}
调用:myfun("luxp", "女", 10, "学生","单身狗",job="程序yuan") 输出: luxp 女 10 ('学生', '单身狗') {'job': '程序yuan'}
实参解包传参
*,**除了在定义函数形参的时候可以使用外,也可以在传递实参的时候使用
#################################################################################
# *params, **kwparams形式实参使用
# myfun具有3个顺序形参
def myfun(name, sex, job):
print(name)
print(sex)
print(job)
# 当以*,**形式传参时, *解包元组形式的实参,**解包字典形式的实参
# 常规调用
myfun("luxp", "BOY", "PYTHON")
# 使用元组解包传递实参
params = ("luxp", "BOY", "PYTHON")
myfun(*params)
# 使用字典解包传递实参
kwparams = {"name":"luxp", "sex":"男","job":"python"}
myfun(**kwparams)
特殊参数限制传参形式
可以在定义函数时使用"/","*"限制传参形式
"/": 表示在此之前的形参项只接受位置传参
"*": 表示在此之后的形参项只接受关键字传参
# def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
# ----------- ---------- ----------
# | | |
# | Positional or keyword |
# | - Keyword only
# -- Positional only
讨论区