6.4 函数参数的种类

在Python中定义函数,可以使用位置参数和关键字参数,两者都可以加默认值,以及组合使用。

6.4.1 位置参数(positional-only,positional-or-keyword)

位置参数是在给函数传参数时,按照顺序,依次传值的参数。

在3.8之后的版本中,positional-only参数与positional-or-keyword参数区分开来,中间使用/分开,positional-only参数在前,positional-or-keyword参数在后。如果没有使用/区分,则都为positional-or-keyword参数。

调用函数时可以根据函数定义的参数位置来传递参数,这时参数的顺序必须一一对应,且少一个参数都不可以。positional-only的参数只能直接传递参数。

而positional-or-keyword参数除了可以直接传递参数之外,还可以使用形如 key=value 的关键字参数来调用函数,这时参数可以不用按照函数定义时的顺序传入。

例如:6.5-位置参数.py

# 位置参数
def fun(a, b, /, c, d):
    print('a =', a)
    print('b =', b)
    print('c =', c)
    print('d =', d)
    print()


print('按位置参数方式传递参数:')
fun(10, 20, 30, 40)

print('按关键字参数方式传递参数:')
fun(100, 200, d=400, c=300)

print('混合传递,位置参数必须在前:')
fun(1000, 2000, 3000, d=4000)

结果为:

按位置参数方式传递参数:
a = 10
b = 20
c = 30
d = 40

按关键字参数方式传递参数:
a = 100
b = 200
c = 300
d = 400

混合传递,位置参数必须在前:
a = 1000
b = 2000
c = 3000
d = 4000

6.4.2 关键字参数(keyword-only)

在参数列表之前加上一个*,则之后的参数必须以关键字参数方式调用。这时只接收已命名的关键字参数,其他不接收。

作用:限制要传入的参数的名字,只能传已命名的关键字参数。

例如:6.6-关键字参数.py

# 关键字参数
def fun(*, a, b, c):
    print('a =', a)
    print('b =', b)
    print('c =', c)
    print()


print('必须以关键字参数方式传递参数:')
fun(b=10, a=20, c=30)

结果为:

必须以关键字参数方式传递参数:
a = 20
b = 10
c = 30

6.4.3 默认值

对一个或多个参数指定一个默认值。这样创建的函数,可以用比定义时允许的更少的参数调用,降低调用函数的难度。

例如:6.7-默认值.py

# 默认值
def fun(a=10, b=20, *, c=30, d=40):
    print('a =', a)
    print('b =', b)
    print('c =', c)
    print('d =', d)
    print()


print('给定参数值传递时,使用传递的参数值:')
fun(100, 200, c=300, d=400)

print('没有给定参数值时,使用默认值作为参数值:')
fun(100, d=400)
fun(b=200, c=300)

结果为:

给定参数值传递时,使用传递的参数值:
a = 100
b = 200
c = 300
d = 400

没有给定参数值时,使用默认值作为参数值:
a = 100
b = 20
c = 30
d = 400

a = 10
b = 200
c = 300
d = 40

6.4.4 可变参数(var-positional,var-keyword)

对于位置参数和关键字参数,可以使用任意数量的参数调用函数。这些可变参数将在位置参数或关键字参数的末尾,它们收集传递给函数的所有剩余的位置参数或关键字参数。

对于位置参数,定义时在参数名前加:*,这些参数会被组装为一个元组,在函数内部使用;对于关键字参数,定义时在参数名前加:**,这些参数会被组装为一个字典,在函数内部使用。

对于可变位置参数,可以以列表或者元组的形式,整体传递;对于可变关键字参数,可以以字典的形式,整体传递。

作用:可以一次给函数传递数目不定的参数。

例如:6.8-可变参数.py

# 可变参数
def fun1(*args):
    print('args =', args)
    print()


def fun2(**kwargs):
    print('kwargs =', kwargs)
    print()


print('可变位置参数,组装为元组:')
fun1()
fun1(10, 20, 30)

print('可以将数据以列表或元组的形式,整体传递:')
data_list = [10, 20, 30]
fun1(*data_list)

print('可变关键字参数,组装为字典:')
fun2()
fun2(a=10, b=20, c=30)

print('可以将数据以字典的形式,整体传递:')
data_dict = {'a': 10, 'b': 20, 'c': 30}
fun2(**data_dict)

结果为:

可变位置参数,组装为元组:
args = ()

args = (10, 20, 30)

可以将数据以列表或元组的形式,整体传递:
args = (10, 20, 30)

可变关键字参数,组装为字典:
kwargs = {}

kwargs = {'a': 10, 'b': 20, 'c': 30}

可以将数据以字典的形式,整体传递:
kwargs = {'a': 10, 'b': 20, 'c': 30}

6.4.5 参数组合

在Python的函数定义中,以上参数形式可以组合使用,但是参数定义的顺序必须是:位置参数–>带默认值的位置参数–>可变位置参数–>命名关键字参数/带默认值的命名关键字参数–>可变关键字参数。

无默认值的位置参数一定要在有默认值的位置参数之前。

只能以关键字参数出现的参数需要一个分隔符*与前面的参数分隔开。如果函数定义中已经有了一个可变位置参数,后面跟着的关键字参数就不再需要分隔符*了。

在Python3.8之后,还可以将仅位置参数(positional-only)放在位置参数(positional-or-keyword)之前,使用 / 分隔开。

在函数调用的时候,Python解释器自动按照参数位置和参数名把对应的参数传进去。

例如:6.9-参数组合.py

# 参数组合
# 位置参数,带默认值的位置参数,可变位置参数,命名关键字参数/带默认值的命名关键字参数,可变关键字参数


def fun(a, /, b=10, *args, c, d=20, **kwargs):
    print('a =', a)
    print('b =', b)
    print('args =', args)
    print('c =', c)
    print('d =', d)
    print('kwargs =', kwargs)
    print()


fun(100, 200, 300, 400, c=1000, d=2000, e=3000, f=4000)

结果为:

a = 100
b = 200
args = (300, 400)
c = 1000
d = 2000
kwargs = {'e': 3000, 'f': 4000}

6.4.6 总结

1,Python的函数具有非常灵活的参数形态,既可以实现简单的调用,又可以传入非常复杂的参数。

2,默认参数一定要用不可变对象,如果使用可变对象,由于只创建一次,所以程序运行时会有逻辑错误!

3,使用*args和**kwargs是约定俗成的习惯写法,不建议使用其他参数名称。

4,注意调用函数时传入可变参数的语法:

可变位置参数:既可以直接传入:func(1, 2, 3),也可以先组装成list或tuple,再通过*args传入:func(*list),func(*tuple);

可变关键字参数:既可以直接传入:func(a=1, b=2),也可以先组装成dict,再通过**kwargs传入:func(**dict)。

5,注意如果以字典形式整体传递的可变关键字参数,这时kwargs获得的字典是主调函数中实参字典的一份拷贝,对kwargs的改动不会影响到主调函数的实参字典。

6,定义命名关键字参数在没有可变位置参数的情况下不要忘了写分隔符*,否则定义的将是位置参数。