2022年 11月 10日

python函数调用位置_Python基础手册23——函数的调用

三、函数的调用

Python 语言中调用函数与在其它高级语言中一样, 函数名加上函数运算符(一对小括号)。 括号之间是所有可选的参数。 即使一个参数也没有, 小括号也不能省略。函数在调用之前必须先定义。

当函数被调用时,其调用者程序停止运行(没有启动子线程调用函数的情况下)直到被调用函数完成了它的工作,并将控制权返回给调用者。

1、函数调用时参数的类型

这里我们主要介绍的是Python的实参。

1.1 位置参数

位置参数必须按照被调用函数中参数定义的准确顺序传递。另外,没有任何默认参数和可变长参数(形参)的话,传入函数的参数(实参)的精确的数目必须和声明参数(形参)的数量一致。

e6a88a6967b1

1.2 关键字参数:通过参数名进行匹配

关键字参数的概念仅仅针对函数的调用。调用者可以定义哪一个参数接收这个值,通过在调用时使用 name=value 这种语法。这样允许参数不按顺序传递,因为解释器能通过给出的关键字来匹配对应的参数(形参)。

e6a88a6967b1

在默认情况下,参数是通过其位置从左至右进行匹配的,而且必须精确的传递和函数头部的标准参数一样多的参数(没有可变长参数的话)。关键字参数允许通过变量名进行匹配,而不是通过位置。

e6a88a6967b1

当关键字参数使用时参数从左至右的关系不在重要了,因为参数是通过变量名进行传递的,而不是根据其位置。甚至在一个调用中混合使用基于位置和基于关键字的参数都是可以的。在这种情况下,所有基于位置的参数首先按照从左至右的顺序匹配头部的参数,之后再进行基于关键字参数的匹配。

e6a88a6967b1

1.3 可变参数解包

我们在调用函数时能够使用 * 号 和 ** 号语法将后面紧跟的参数集合打散,分解成参数传递给调用的函数。

(1)解包一个序列

调用函数时使用的 * 号(实参的)与在函数定义头部的 * 号(形参的)恰恰相反:在函数定义的头部它意味着这个参数可以收集任意多的参数,而在调用时意味着他会解包参数(实参)的集合,这个参数可以传递任意多的参数。

e6a88a6967b1

(2)解包一个字典

在函数调用时,使用 ** 号会以键值对的形式解包一个字典,使其成为独立的关键字参数。

e6a88a6967b1

我们在调用中能够以非常灵活的方式混合位置参数以及关键字参数。

e6a88a6967b1

2、传递函数

在 Python 中函数对象是可以被引用的(函数名就是对函数对象的引用),也可以作为参数传入函数并被返回,以及作为列表和字典等容器对象的元素。

函数有一个独一无二的特征使它同其他对象区分开来,那就是函数是可调用的。

因为所有的对象都是通过引用来传递的,函数也不例外。当对一个变量赋值时,实际是将相同对象的引用赋值给这个变量。如果对象是函数的话,这个对象所有的别名都是可调用的。

e6a88a6967b1

当我们把 foo 赋值给 bar 时,bar 和 foo 引用了同一个函数对象,所以能以和调用 foo() 相同的方式来调用 bar()。确定你明白 “foo”(函数对象的引用)和 “foo()”(函数对象的调用)的区别。

我们甚至可以把函数作为参数传入其他函数来进行调用。

e6a88a6967b1

以及把函数对象返回

e6a88a6967b1

3、参数的排列顺序

在函数调用中,参数(实参)必须以此顺序出现:任何位置参数(value),后面跟着任何关键字参数(name=value),和 *sequence 形式的组合,后面跟着 **dict 形式。在调用和函数头部中,如果出现 **arg 形式的话,都必须出现在最后。

如果你使用了任何其他的顺序混合了参数,你可能得到一个语法错误,因为其他顺序的混合会产生歧义。虽然有时我们没有严格的按照此顺序来传递实参, 比如我们上面的例子中(只是为了掩饰,或娱乐 🙂 ),而且解释器并没有报错。但是我们强烈的建议你按照规定的顺序来传递参数。

四、参数的匹配规则

匹配语法:

位置:

解释:

def func(name)

函数

标准参数:通过位置或变量名进行匹配

def func(name=value)

函数

默认参数,如果没有在调用中传递的话,将使用默认值

def func(*args)

函数

匹配并收集(在元组中)所有没有匹配的位置参数

def func(**kwargs)

函数

匹配并收集(在字典中)所有没有匹配的关键字参数

def func(*args, name=value)

函数

Keyword-only参数:必须在调用中按照关键字传递

func(value)

调用者

位置参数:通过位置进行匹配

func(name=value)

调用者

关键字参数:通过变量名匹配

func(*sequence)

调用者

以name传递所有的对象,并作为独立的基于位置的参数

func(**dict)

调用者

以name传递所有的关键字/值,并作为独立的关键字参数

在函数定义的头部,一个简单的标准参数是通过位置参数或关键字参数进行匹配的(取决于调用者是如何传递给它参数的),name=value 的形式定义了默认参数。*args的形式收集了任意的额外不匹配的位置参数到元祖中,并且 **kwargs 的形式将会收集额外的关键字参数到字典中。跟在 *args 或一个单独的 * 之后的、任何 name=value 的形式的参数名称,都是 keyword-only 参数,并且必须在调用中按照关键字传递。

在函数的调用中(在表中的后4行),位置参数简单的按照从左至右的顺序进行匹配,关键字参数告诉 Python 依照变量名进行匹配。在调用中使用 *sequence 或者 **dict允许我们在一个序列或字典中相应地封装任意多的位置参数或者关键字参数,并且在将他们传递给函数的时候,将它们解包为分开的、单个的参数。

Python 内部是使用以下的步骤来在赋值前进行参数匹配的:

如果存在解包函数,将其打散,按照位置参数在前,关键字参数在最后的顺序排序;

通过位置分配位置参数;

通过匹配变量名分配关键字参数;

其他额外的非关键字参数分配到 *args 元组中

其他额外的关键字参数分配到 **kwargs 字典中

未分到到值的默认参数将使用默认值。

在这之后,Python检测来确保每个参数只传入了一个值,如果不是这样的话,将会发生错误。当所有的匹配都完成了,Python把传递给参数名的对象赋值给他们。

Python真正使用的真正的匹配算法更复杂一些(例如,它必需考虑keyword-only参数),因此,要了解更为详细的介绍,请参考Python的标准语言手册。