函数形参

形参是函数定义中指定的参数名称。指定某个参数的形式,决定了该形参在函数调用时,可以接受实参的方式。关于实参详见 函数调用

因而形参分为五种:

  • 位置或关键字:指定一个可以作为 位置参数 传入也可以作为 关键字参数 传入的实参。这是默认的形参类型,但有默认值的形参必须置于无默认值的形参之后。
def f(a,b=None):
    print(f'a={a},b={b}')
# 位置实参传入
f(1,2)
# 关键字实参传入
f(b=2,a=1)
a=1,b=2
a=1,b=2
  • 仅限位置:指定一个只能通过位置传入的参数。仅限位置形参通过在函数定义的形参之后包含一个 / 字符来定义。/ 之前的参数为仅限位置形参,之后的形参为默认形参类型。有默认值的形参也必须置于无默认值的形参之后。
def f(a,b=None,/,c=None): # 因为 b 有默认值,c 必须要有默认值
    print(f'a={a},b={b},c={c}')
# 按位置传参调用
f(1,2,c=3)
# 关键字传参则不允许
f(a=1,b=2)
a=1,b=2,c=3


---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-6-52b3afdaad4c> in <module>
      4 f(1,2,c=3)
      5 # 关键字传参则不允许
----> 6 f(a=1,b=2)


TypeError: f() got some positional-only arguments passed as keyword arguments: 'a, b'
  • 仅限关键字:指定一个只能通过关键字传入的参数。仅限关键字形参可通过在函数定义的形参中包含单个 可变位置形参 或者在形参之前放一个 * 来定义。可变位置形参 或 * 之后的参数为仅限关键字形参。
def f(*a,b=None,c=None): # b 和 c 必须有默认值
    print(f'a={a},b={b},c={c}')
# 位置传参将被解读为可变位置参数
f(1,2,3)
# 关键字传参
f(1,b=2,c=3)
a=(1, 2, 3),b=None,c=None
a=(1,),b=2,c=3
def f(*,a,b=None,c):
    print(f'a={a},b={b},c={c}')
# 关键字传参
f(b=2,a=1,c=3)
# 位置传参不允许
f(1,2,3)
a=1,b=2,c=3


---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-1-9d584fa28622> in <module>
      4 f(b=2,a=1,c=3)
      5 # 位置传参不允许
----> 6 f(1,2,3)


TypeError: f() takes 0 positional arguments but 3 were given
  • 可变位置:指定一个可以接受任意数量的位置参数传入的参数。这种形参可通过在形参名称前加缀 * 来定义,并将接受到的参数封装成一个元组。该参数如果接受到了实参,它前面的参数必须为仅限位置参数。
def f(a,b=None,*c):
    print(f'a={a},b={b},c={c}')
# c 没有接受参数
f(1); f(b=2,a=1)
# c 接受到了参数
f(1,2,3,4,5)
a=1,b=None,c=()
a=1,b=2,c=()
a=1,b=2,c=(3, 4, 5)
  • 可变关键字:指定一个可以接受任意数量的关键字参数的参数。这种形参可通过在形参名称前加缀 ** 来定义,并将接受到的参数封装成一个字典。
def f(a,b=None,**c):
    print(f'a={a},b={b},c={c}')

f(1,2,c=3,d=4)
f(d=4,b=2,a=1,c=3)
a=1,b=2,c={'c': 3, 'd': 4}
a=1,b=2,c={'d': 4, 'c': 3}

带默认值的参数,可变位置参数和可变关键字参数,调用函数时可以不传参。

def f(a=1,*b,c=3,**d):
    print(f'a={a},b={b},c={c},d={d}')
f()
a=1,b=(),c=3,d={}

默认值只会执行一次,这条规则很重要。如果参数有默认值且为可变对象,则需要做必要的限制:

def f(a=[]):
    print(id(a))
    a.append(1)
    print(a)
f() # 多次调用会引用参数指向的同一个对象
f()
2399568560064
[1]
2399568560064
[1, 1]
# 可以拷贝一个副本
def f(a=[]):
    b = a.copy()
    b.append(1)
    print(b)
f()
f()
[1]
[1]
# 或者修改参数
def f(a=None):
    if a == None:
        a = []
        a.append(1)
    else:
        a.append(1)
    print(a)
    
f()
f([])
[1]
[1]

函数的形参可以使用标注,标注的语法是参数后面接一个冒号 :,然后接一个表达式(任意表达式),通常用来指明应该(不是必须)传递什么类型的参数等。标注提高了代码的可读性:

# a 标注为字符串类型,b 标注为整数,并设置默认值 2
def f(a:str,b:int=2):
    return a*b
f('Hi',3)
'HiHiHi'
def f(a:'字符串',b:'整数'=2):
    return a*b
f('Hi')
'HiHi'