同大多数语言相同,我们用一对圆括号调用函数。实际上,有些人认为(())是一个双字符操作符。正如你可能意识到的,任何输入的参数都必须放置在括号中。作为函数声明的一部分,括号也会用来定义那些参数。虽然我们没有正式地学习类和面向对象编程,但你将会发现在Python中,函数的操作符同样用于类的实例化。
关键字参数的概念仅仅针对函数的调用。这种理念是让调用者通过函数调用中的参数名字来区分参数。这样规范允许参数缺失或者不按顺序,因为解释器能通过给出的关键字来匹配参数的值。
举个简单的例子,比如有一个函数foo(),伪代码如下:
再举个更实际的例子,假设你有一个函数叫做net_conn(),需要两个参数host和port:
只要按照函数声明中参数定义的顺序,输入恰当的参数,自然就可以调用这个函数:
host参数得到字符串‘kappa’,port参数得到整型8080.当然也可以不按照函数声明中的参数顺序输入,但是要输入相应的参数名,如下例:
当参数允许“缺失”的时候,也可以使用关键字参数。这取决于函数的默认参数,我们将在下一小节对它进行介绍。
默认参数就是声明了默认值的参数。因为给参数赋予了默认值,所以,在函数调用时,不向该参数传入值也是允许的。我们将在11.5.2小节对默认参数进行更全面的介绍。
Python同样允许程序员执行一个没有显式定义参数的函数,相应的方法是通过一个把元组(非关键字参数)或字典(关键字参数)作为参数组传递给函数。我们将在本章中讨论这两种形式。基本上,你可以将所有参数放进一个元组或者字典中,仅仅用这些装有参数的容器来调用一个函数,而不必显式地将它们放在函数调用中:
其中的tuple_grp_nonkw_args是以元组形式体现的非关键字参数组,dict_grp_kw_args是装有关键字参数的字典。正如我们已经提到的,我们将在这章对这两者进行全面介绍,现在你只需知道,存在这样的特性允许你把变量放在元组和/或者字典里,并在没有显式地对参数进行逐个声明的情况下,调用函数。
实际上,你也可以给出形参!这些参数包括标准的位置参数和关键字参数,所以在Python中允许的函数调用的完整语法为:
该语法中的所有的参数都是可选的——从参数传递到函数的过程来看,在单独的函数调用时,每个参数都是独立的。这可以有效地取代apply()内建函数。(在Python 1.6版本之前,这样的参数对象只能通过apply()函数来调用)。
1.例子
在子11.1里的数学游戏中,我们用函数调用转换来生成一个有两个子项的参数列表,并把这个列表发送给合的适算术函数(我们也会指出在原来版本中哪些地方会用到apply())。
easyMath.py程序是一个儿童算术游戏,可以随机选择算术加减法。我们通过函数add(),sub()等价+-操作符,这两者都可以在operator模块中找到。接着我们生成一个参数列表(该列表只有2个参数,因为这些是二元操作符/运算)。接着选择任意的数字作为算子。因为我们没打算在这个程序的基础版本中支持负数,所以我们将两个数字的列表按从大到小的顺序排序,然后用这个参数列表和随机选择的算术操作符去调用相对应的函数,最后获得问题的正确解答。
随机选择数字以及一个算术函数,显示问题,以及验证结果。在3次错误的尝试以后给出结果,等到用户输入一个正确的答案后便会继续运行。
例11.1 算术游戏(easyMath.py)
2.逐行解释
1 ~ 4行
我们的代码从通常的unix启动行开始,接着从operator和random模块中,导入我们会用到的函数。
6 ~ 7行
在这个应用程序中我们用的全局变量有:一个包含了操作符和与其相关联的函数的集合(字典),一个决定在给出正解之前,用户有多少次机会尝试给出答案的整型变量。函数字典的键值是操作符的符号,程序通过查字典找到合适的算术函数。
9 ~ 28行
doprob()函数是应用程序的核心引擎。该函数随机选择一个操作并生成两个操作数,同时为了避免减法问题中的负数问题,将这两个算子按大到下进行排序。然后用这些值调用一个数学函数,计算出正确的解。接着用一个等式来提示用户输入并给用户3次机会来输入一个正确的答案。
第10行用了random.choice()函数。它用于获取一个序列——我们案例中操作符号的字符串——并随机返回其中的元素。
第11行用了一个列表解析来随机地给我们的练习选择两个数。这个例子非常的简单以至于我们可以仅仅用两次randint()来获得我们的操作数,比如,nums= [randint(1,10),randint(1,10)],但是为了让你能看看列表解析的又一个例子,我们没有这样做,而且使用列表解析更易于扩展和升级,比如获得更多的数,这与我们使用循环来代替剪切和粘贴的原因相似。
第12行只能在Python2.4以及更新的版本中运行,因为list.sort()方法原本不支持倒转的标志位。如果你使用的是更早一点的Python版本,你可以:
• 增加一个反序的比较函数来获得倒转的排序,如
• 或者在nums.sort()后调用nums.reverse()
如果你之前没有看见过lambda,不用害怕。我们会在这章对lambda进行详述,而现在,你可以认为它是一个单行的匿名函数。
如果你正使用1.6以前的python,那第13行是可能会用到apply()。对合适运算函数的调用要这样写apply(ops[op], nums),而不是ops[op](*nums)。
16〜28行描述了用来处理有效和无效输入的控制循环。while循环是无限循环,直到有正确答案输入或者允许尝试的次数(我们的程序中设定为3次)被耗尽才终止运行。这允许程序接受不合法的输入,比如非数字或者各种键盘的控制字符。一旦用户超过了尝试最大的次数,程序就会给出答案并“强制”用户给出正确的答案,只有给出答案,程序才会向下进行。
30 ~ 41行
程序的主入口是main(),如果直接运行脚本,程序将自项向下的运行。如果被作为模块导入,导入者要么调用doprob()函数来开始执行,要么调用main()来进入程序控制。main()简单地调用doprob()使用户与脚本的主要功能进行交互,并负责提示用户退出或者尝试下一个问题。
因为数值和操作符都是随机选择的,每次运行easyMath.py的结果应该都是不一样的。这是我们今天的得到的(噢,你的答案也可能不一样):