11.5 Formal Arguments

Python函数的形参集合由在调用时要传入函数的所有参数组成,这参数与函数声明中的参数列表精确地配对。这些参数包括了所有必要参数(以正确的定位顺序来传入函数的)、关键字参数(以顺序或者不按顺序传入,但是带有参数列表中曾定义过的关键字)和所有含有默认值,函数调用时不必要指定的参数。(声明函数时创建的)局部命名空间为各个参数值,创建了一个名字。一旦函数开始执行,就能访问这个名字。

11.5.1 位置参数

这些我们都是熟悉的标准化参数。位置参数必须以在被调用函数中定义的准确顺序来传递。另外,没有任何默认参数(见下一个部分)的话,传入函数(调用)的参数的精确的数目必须和声明的数字一致。

11.5 Formal Arguments - 图1

foo()函数有一个位置参数。那意味着任何对foo()的调用必须有唯一的一个参数,不多,不少。否则你会频频看到TypeError。看看,Python的错误是多么具有信息性的。作为一个普遍的规则,无论何时调用函数,都必须提供函数的所有位置参数。可以不按位置地将关键字参数传入函数,给出关键字来匹配其在参数列表中的合适的位置是被准予的(可以回顾11.2.2小节)。

由于默认参数的特质,他们是函数调用的可选部分。

11.5.2 默认参数

对于默认参数如果在函数调用时没有为参数提供值则使用预先定义的默认值。这些定义在函数声明的标题行中给出。也支持默认参数,和Python有同样的语法:参数名等号默认值。这个从语法上来表明如果没有值传递给那个参数,那么这个参数将取默认值。

Python中用默认值声明变量的语法是所有的位置参数必须出现在任何一个默认参数之前。

11.5 Formal Arguments - 图2

每个默认参数都紧跟着一个用默认值的赋值语句。如果在函数调用时没有给出值,那么这个赋值就会实现。

1.为什么用默认参数

默认参数让程序的健壮性上升到极高的级别,因为它们补充了标准位置参数没有提供的一些灵活性。这种简洁极大的帮助了程序员。当少几个需要操心的参数时候,生活不再那么复杂。这在一个程序员刚接触到一个API接口时,没有足够的知识来给参数提供更对口的值时显得尤为有帮助。

使用默认参数的概念与在你的电脑上安装软件的过程类似。一个人会有多少次选择默认安装而不是自定义安装?我可以说可能几乎都是默认安装。这既方便、易于操作,又能节省时间。总是选择自定义安装的只是少数人。

另外一个让开发者受益的地方在于,开发者能更好地控制为顾客开发的软件。当提供了默认值的时候,他们可以精心选择“最佳”的默认值,所以用户不需要马上面对繁琐的选项。随着时间流逝,当用户对系统或者API越来越熟悉的时候,他们最终能自行给出参数值,便不再需要使用“学步车”了。

下面这个例子中默认参数派得上用场,并在日益增长的电子商务中多少有些用处。

11.5 Formal Arguments - 图3

在上面个例子中,taxMe()函数以一个项目的成本输入参数,计算出附加了销售税的销售价格。成本是一个必需的参数,但税率是一个默认参数(在我们的例子中为8.25%)。或许你是一个在线零售商,生意上的大部分客户来自相同的州或者国家。不同地方税率的顾客期望看见他们与当地销售税率相对应的购买价格总量。为了覆盖默认的税率,你所要做的就是提供一个参数值,比如在上面的例子中的taxMe(100,0.05)。通过指定5%税率,你提供了一个参数作为税率参数,所以覆盖或者说绕过了0.0825的默认值。

所有必需的参数都要在默认参数之前。为什么?简单说来就是因为它们是强制性的,但默认参数不是。从语法构成上看,对于解释器来说,如果允许混合模式,确定什么值来匹配什么参数是不可能的。如果没有按正确的顺序给出参数,就会产生一个语法错误。

11.5 Formal Arguments - 图4

让我们再看下关键字参数,用我们的老朋友net_conn()。

11.5 Formal Arguments - 图5

读者应该还记得,如果命名了参数,这里可以不按顺序给出参数。由于有了上述声明,我们可以做出如下(规则的)位置或者关键字参数调用:

11.5 Formal Arguments - 图6

然而,如果我们将默认参数引入这个等式,情况就会不同,虽然上面的调用仍然有效。让我们修改下net_conn()的声明以使端口参数有默认值80,再增加另外的名为stype(服务器的类型)默认值为’tcp’的参数:

11.5 Formal Arguments - 图7

我们已经扩展了调用.net_conn()的方式。以下就是所有对net_conn()有效的调用:

11.5 Formal Arguments - 图8

在上面所有的例子中,我们发现什么是一直不变的?唯一的必须参数,host。host没有默认值,所以他必须出现在所有对net_conn()的调用中。关键字参数已经被证明能给不按顺序的位置参数提供参数,结合默认参数,它们同样也能被用于跳过缺失参数,上面例子就是极好的证据。

2.默认函数对象参数举例

我们现在将给出另外一个证明默认参数会让人受益的例子。grabWeb.py脚本,在例子11.4中给出,是一个主要目的是从互联网上抓取一个Web页面并暂时储存到一个本地文件中用于分析的简单脚本。这类程序能用来测试Web站点页面的完整性或者能监测一个服务器的负载(通过测量可链接性或者下载速度)。process()函数可以做我们想要的任何事,表现出了无限种的用途。我们为这个练习选择的用法是显示从Web页面上获得的第一和最后的非空格行。虽然在现实中这个特别的例子或许没有多少用处,但是你可以以这段代码为基础,举一反三。

这段脚本下载了一个Web页面(默认为本地的万维网服务器)并显示了HTML文件的第一个以及最后一个非空格行。由于download()函数的双默认参数允许用不同的urls或者指定不同的处理函数来进行覆盖,灵活性得倒了提高。

例11.4 抓取Web页面(grabWeb.py)

11.5 Formal Arguments - 图9

11.5 Formal Arguments - 图10

在我们的环境下运行这个脚本会得到如下的输出,虽然你的内容是绝对不同的,因为你将浏览一个完全不同的网页。

11.5 Formal Arguments - 图11