Python:函数调用的实参

张开发
2026/5/31 0:24:10 15 分钟阅读
Python:函数调用的实参
相关阅读Python专栏https://blog.csdn.net/weixin_45791458/category_12403403.html调用就是给一个可调用对象附带一组可能为空的参数并执行它。这里的可调用对象不仅包括普通函数也包括内置函数、方法、类以及任何实现了__call__()方法的对象。它的语法BNF范式如下所示。关于BNF规则的基本含义可以参考前面的文章。call :: primary ( [argument_list [,] | comprehension] ) argument_list :: positional_arguments [, starred_and_keywords] [, keywords_arguments] | starred_and_keywords [, keywords_arguments] | keywords_arguments positional_arguments :: positional_item (, positional_item)* positional_item :: assignment_expression | * expression starred_and_keywords :: (* expression | keyword_item) (, * expression | , keyword_item)* keywords_arguments :: (keyword_item | ** expression) (, keyword_item | , ** expression)* keyword_item :: identifier expression其中primary表示“原型”也就是编程语言里结合最紧密的一类表达式。它可以有多种具体形式例如一个简单的函数名标识符。不过无论它写成什么形式最终都必须被求值为一个可调用对象。比如用户自定义函数、内置函数、内置对象的方法、类对象、实例方法、实现了__call__()方法的对象都属于可调用对象。另外在真正执行调用之前所有参数表达式都会先被求值。调用时的参数列表由一对圆括号()包围是一个可选部分。括号中的内容可以是argument_list或者一个生成器推导式。实际开发中更常见的是argument_list它主要有三种形式1、先是位置实参列表后面可以跟“带*的实参和关键字实参列表”最后还可以继续跟“带**的实参和关键字实参列表”。2、没有普通的位置实参列表直接从“带*的实参和关键字实参列表”开始后面仍然可以选择再跟“带**的实参和关键字实参列表”。3、参数列表中只包含“带**的实参和关键字实参列表”。此外参数列表最后允许多写一个逗号这不会影响语义。如果调用中存在关键字实参那么Python会按照一定规则把这些实参与函数定义中的形参一一对应起来。可以把这个过程理解为先为函数的形式参数建立一张“待填充表”每个形参占据一个空位。接着按如下步骤处理首先如果有N个位置实参那么它们会依次填入前N个空位。然后再依次处理关键字实参。对于每个关键字实参Python 会根据关键字的名字去寻找同名的形式参数并把它填入对应的空位中。这里有几个结果需要注意1、如果某个空位已经被填过再次填入就会抛出TypeError。2、如果关键字找不到对应的形参名也会抛出TypeError除非函数定义中存在**kwargs这样的参数来接收它们。3、当所有实参都处理完之后仍然没有填充的空位会尝试使用函数定义时提供的默认值来补齐。4、如果某个未填充的形参既没有传参也没有默认值那么同样会抛出TypeError。这里还要特别注意一个常见问题函数默认值是在函数定义时计算的而不是在每次调用时重新计算的。这意味着如果把列表、字典这类可变对象作为默认值那么多个调用之间可能会共享同一个对象。这样往往会带来意料之外的结果因此通常应尽量避免这样写。下面展示了一个例子。def add_item(item, lst[]): lst.append(item) return lst print(add_item(1)) print(add_item(2)) print(add_item(3)) 输出 [1] [1, 2] [1, 2, 3] 而不是 [1] [2] [3]如果某个关键字实参找不到对应的形式参数名通常会报错。但如果函数定义中使用了**expression对应的形参形式那么这些“多出来的关键字实参”就不会报错而是会被统一收集到一个字典中。这个字典满足下面的规则键是关键字参数名值是对应的参数值如果没有多余的关键字实参那么得到的是一个新的空字典。如果函数调用中出现了*expression这样的语法那么这个expression必须求值为一个可迭代对象。Python 会把这个可迭代对象中的元素逐个取出并 把它们当作位置实参参与调用。对于f(x1, x2, *y, x3, x4)调用如果y求值为一个序列y1, y2, ..., ym则它就等价于一个带有M4个位置参数f(x1, x2, *y, x3, x4)的调用。虽然一般来说位置实参不能写在关键字实参之后但*expression是个例外。原因在于它在语义上会先被展开成位置实参然后再与关键字实参一起参与参数绑定。也就是说即使它书写顺序上出现在关键字实参后面它在处理阶段仍然会先以“位置参数”的身份参与匹配。 因此def f(a, b): print(a, b) f(b1, *(2,)) # 一般情况下位置参数不能在关键词参数后但由*中解析的位置参数除外 输出2 1 # 2会被解包并作为位置实参填充形参a f(a1, *(2,)) # 2被解包并作为位置实参填充形参a之后关键词实参a1又填充了a因此报错 输出Traceback (most recent call last): File stdin, line 1, in module TypeError: f() got multiple values for keyword argument a f(1, *(2,)) 输出1 2下面的文章还介绍了函数定义的形参相关问题欢迎浏览。Python函数定义的形参https://blog.csdn.net/weixin_45791458/article/details/132889428?csdn_share_tail%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22132889428%22%2C%22source%22%3A%22weixin_45791458%22%7D

更多文章