请稍候,加载中....

传参注意事项

函数形参根据传入的方式分为:

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)

例2:以字典类型值作为实参

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

Python学习手册-