鉴于函数定义中可能包含多个形参,因此函数调用中也可能包含多个实参。向函数传递实参的方式很多,可使用位置实参(Positional Arguments),这要求实参的顺序与形参的顺序相同;也可使用关键字实参(Keyword Arguments),其中每个实参都由变量名和值组成;还可使用列表和字典。下面来依次介绍这些方式。

8.2.1 位置实参(Positional Arguments)

你调用函数时,Python必须将函数调用中的每个实参都关联到函数定义中的一个形参。为此,最简单的关联方式是基于实参的顺序。这种关联方式被称为位置实参

为明白其中的工作原理,来看一个显示足球运动员信息的函数。这个函数指出一个足球运动员踢哪个位置以及TA叫什么名字,如下所示:

  1. # footballer.py
  2. def footballer_info(name, position):
  3. print(f"{name}是一个很好的足球运动员,他踢{position}位置!")
  4. footballer_info('Thomas', '中场')

这个函数的定义表明,它需要一个球员名字和他踢的位置。调用footballer_info()时,需要按顺序提供一个名字和一个位置。例如,在前面的函数调用中,实参’Thomas’存储在形参name中,而实参’中场’存储在形参position中。在函数体内,使用了这两个形参来显示球员的信息。

输出描述了一位名叫Thomas的球员的信息:


Thomas是一个很好的足球运动员,他踢中场位置!


调用函数多次

你可以根据需要调用函数任意次。要再描述一位球员,只需再次调用footballer_info()即可:

  1. # footballer.py
  2. def footballer_info(name, position):
  3. print(f"{name}是一个很好的足球运动员,他踢{position}位置!")
  4. footballer_info('Thomas', '中场')
  5. footballer_info('Haaland', '中锋')

第二次调用footballer_info()函数时,我们向它传递了实参’Haaland’和’中锋’。与第一次调用时一样,Python将实参’Haaland’关联到形参name,并将实参’中锋’关联到形参position。与前面一样,这个函数完成其任务,但打印的是一位名叫Haaland的球员的信息。至此,我们有一位名叫Thomas的球员,还有一位名叫Haaland的球员:


Thomas是一个很好的足球运动员,他踢中场位置!
Haaland是一个很好的足球运动员,他踢中锋位置!


调用函数多次是一种效率极高的工作方式。我们只需在函数中编写描述球员的代码一次,然后每当需要描述新球员时,都可调用这个函数,并向它提供新球员的信息。即便描述球员的代码增加到了10行,你依然只需使用一行调用函数的代码,就可描述一位新球员。

在函数中,可根据需要使用任意数量的位置实参,Python将按顺序将函数调用中的实参关联到函数定义中相应的形参。

位置实参的顺序很重要

使用位置实参来调用函数时,如果实参的顺序不正确,结果可能出乎意料:

  1. # footballer.py
  2. def footballer_info(name, position):
  3. print(f"{name}是一个很好的足球运动员,他踢{position}位置!")
  4. footballer_info('中场', 'Thomas')

在这个函数调用中,我们先指定位置,再指定名字。由于实参’中场’在前,这个值将存储到形参name中;同理,’Thomas’将存储到形参position中。结果是我们得到了一个名为’中场’的Thomas:


中场是一个很好的足球运动员,他踢Thomas位置!


如果结果像上面一样搞笑,请确认函数调用中实参的顺序与函数定义中形参的顺序一致。

8.2.2 关键字实参

关键字实参是传递给函数的名称—值对。你直接在实参中将名称和值关联起来了,因此向函数传递实参时不会混淆。关键字实参让你无需考虑函数调用中的实参顺序,还清楚地指出了函数调用中各个值的用途。

  1. # footballer.py
  2. def footballer_info(name, position):
  3. print(f"{name}是一个很好的足球运动员,他踢{position}位置!")
  4. footballer_info(position='中场', name='Thomas')

函数footballer_info()还是原来那样,但调用这个函数时,我们向Python明确地指出了各个实参对应的形参。看到这个函数调时,Python知道应该将实参’中场’和’Thomas’分别存储在形参position和name中。输出正确无误,它指出我们有一位名叫Thomas的中场球员。

关键字实参的顺序无关紧要,因为Python知道各个值该存储到哪个形参中。下面两个函数调用是等效的:

  1. footballer_info(position='中场', name='Thomas')
  2. footballer_info(name='Thomas', position='中场')

注:使用关键字实参时,务必准确地指定函数定义中的形参名。

8.2.3 默认值

编写函数时,可给每个形参指定默认值。在调用函数中给形参提供了实参时,Python将使用指定的实参值;否则,将使用形参的默认值。因此,给形参指定默认值后,可在函数调用中省略相应的实参。使用默认值可简化函数调用,还可清楚地指出函数的典型用法。

例如,如果你发现调用footballer_info()时,描述的大都是中场球员,就可将形参position的默认值设置为’中场’。这样,调用footballer_info()来描述球员时,就可不提供这种信息:

  1. # footballer.py
  2. def footballer_info(name, position='中场'):
  3. print(f"{name}是一个很好的足球运动员,他踢{position}位置!")
  4. footballer_info('Thomas')

这里修改了函数footballer_info()的定义,在其中给形参position指定了默认值’中场’。这样,调用这个函数时,如果没有给position指定值,Python将把这个形参设置为’中场’:


Thomas是一个很好的足球运动员,他踢中场位置!


注:使用默认值时,在形参列表中必须先列出没有默认值的形参,再列出有默认值的实参。这让Python依然能够正确地解读位置实参。

8.2.4 等效的函数调用

鉴于可混合使用位置实参、关键字实参和默认值,通常有多种等效的函数调用方式。请看下面的函数footballer_info()的定义,其中给一个形参提供了默认值:

  1. # footballer.py
  2. def footballer_info(name, position='中场'):
  3. print(f"{name}是一个很好的足球运动员,他踢{position}位置!")

基于这种定义,在任何情况下都必须给name提供实参;指定该实参时可以使用位置方式,也可以使用关键字方式。如果要描述的动物不是中场,还必须在函数调用中给position提供实参;同样,指定该实参时可以使用位置方式,也可以使用关键字方式。

下面对这个函数的所有调用都可行:

  1. # 一位名叫Thomas的中场
  2. footballer_info('Thomas')
  3. footballer_info(name='Thomas')
  4. # 一位名叫Haaland的中锋
  5. footballer_info('Haaland', '中锋')
  6. footballer_info(name='Haaland', position='中锋')
  7. footballer_info(position='中锋', name='Haaland')

注:使用哪种调用方式无关紧要,只要函数调用能生成你希望的输出就行。使用对你来说最容易理解的调用方式即可。

8.2.5 避免实参错误

等你开始使用函数后,如果遇到实参不匹配错误,不要大惊小怪。你提供的实参多于或少于函数完成其工作所需的信息时,将出现实参不匹配错误。例如,如果调用函数footballer_info()时没有指定任何实参,结果将如何呢?

  1. # footballer.py
  2. def footballer_info(name, position='中场'):
  3. print(f"{name}是一个很好的足球运动员,他踢{position}位置!")
  4. footballer_info()

Python发现该函数调用缺少必要的信息,而traceback指出了这一点:


—————————————————————————————————————-
TypeError Traceback (most recent call last)
/var/folders/nx/rd2v8dvx6jb6df_ww841n9580000gn/T/ipykernel_95964/26816302.py in
3 print(f”{name}是一个很好的足球运动员,他踢{position}位置!”)
4
——> 5 footballer_info()

TypeError: footballer_info() missing 1 required positional argument: ‘name’


Python读取函数的代码,并指出我们需要为哪些形参提供实参,这提供了极大的帮助。这也是应该给变量和函数指定描述性名称的另一个原因;如果你这样做了,那么无论对于你,还是可能使用你编写的代码的其他任何人来说,Python提供的错误消息都将更有帮助。

如果提供的实参太多,将出现类似的traceback,帮助你确保函数调用和函数定义匹配。