6.2 灵活即强大
视频讲解
有时候,评论一种编程语言是否优秀,往往是看它是否灵活。灵活并非意味着无所不能、无所不包,那样就会显得庞大和冗杂。灵活应该表现为多变,如前面学到的参数,函数因参数而灵活。如果没有参数,一个函数就只能死板地完成一个功能、一项任务。
6.2.1 形参和实参
参数从调用的角度来说,分为形式参数(parameter)和实际参数(argument)(注:本书后面简称为“形参”和“实参”)。与绝大多数编程语言一样,形参指的是函数定义的过程中小括号里的参数,而实参则指的是函数在被调用的过程中传递进来的参数。
举个例子:
sayHi(name)中的name是一个形参,因为它只是代表一个位置、一个变量名;而调用sayHi("小甲鱼")传递的"小甲鱼"则是一个实参,因为它是一个具体的内容,是赋值到变量name中的值。
6.2.2 函数文档
给函数写文档是为了让后人可以更好地理解你的函数设计逻辑,对于一名优秀的程序员来说,养成编写函数文档的习惯无疑是非常必要的。因为在实际开发中,个人的工作量和能力确实相当有限,因此中、大型的程序永远都是团队来完成的。大家的代码要相互衔接,就需要先阅读别人提供的文档,因此适当的文档说明非常重要。而函数文档的作用是描述该函数的功能以及一些注意事项:
例如该汇率转换函数,因为汇率其实每天都在变化,所以如果没有注明指定汇率的日期,就可能会导致数据产生偏差。
可以看到,函数开头的几行字符串并不会被打印出来,但它将作为函数的一部分存储起来。这个字符串称为函数文档,它的功能与代码注释是一样的。
有读者可能会说,既然一样,搞那么复杂干啥呀?其实也不是完全一样,函数的文档字符串可以通过特殊属性_ _doc_ _获取(注:_ _doc_ _两边分别是两条下画线):
另外,当想使用一个BIF却又不确定其用法的时候,可以通过help()函数来查看函数的文档:
6.2.3 关键字参数
前面在定义函数的时候,就已经把参数的名字和位置确定下来,Python中这类位置固定的参数称为位置参数。对于函数的调用者来说,只需要知道按照顺序传递正确的参数就可以了。
可是有些啥时候,粗心的程序员很容易会搞乱位置参数的顺序,以至于函数无法按照预期实现。对于这类情况,使用关键字参数就很有用了。
举个例子:
关键字参数其实就是在传入实参时明确指定形参的变量名,其特点就是参数之间不存在先后顺序。尽管使用这种技巧要多输入一些字符,但随着程序规模越大、参数越多的时候,关键字参数起到的作用就越明显。毕竟宁可多输入几个字符,也不希望出现料想不及的BUG。
另外,在调用函数的时候,位置参数必须在关键字参数的前面,否则就会出错:
>>> eat(something="蛋糕", "小甲鱼") SyntaxError: positional argument follows keyword argument
6.2.4 默认参数
Python的函数允许为参数指定默认的值,那么在函数调用的时候如果没有传递实参,则采用默认参数值:
可以看到,默认参数使得函数的调用更加便捷了。这就像日常安装应用程序,可以选择自定义或者默认安装,我想绝大多数普通用户都会选择默认安装的,对吧?
结合默认参数和关键字参数,可以使函数的调用变得非常灵活:
另外,在定义函数的时候,位置参数必须在默认参数的前面,否则就会出错:
>>> def watchMovie(name="小甲鱼", cigarette=True, beer=True, girlfriend): pass SyntaxError: non-default argument follows default argument
6.2.5 收集参数
这个名字看起来比较新鲜,其实大多数时候它也被称为可变参数。有时候,可能函数也不知道调用者实际上会传入多少个实参,这看起来很可笑,对吗?其实不然,例如我们熟悉的print()函数就是这样:
>>> print(1, 2, 3, 4, 5) 1 2 3 4 5 >>> print("I", "Love", "FishC") I Love FishC
若实参个数不确定,在定义函数的时候,形参就可以使用收集参数来“搞定”。而语法也很简单,仅需要在参数前面加上星号(*)即可:
其实大家仔细思考后也不难理解,Python就是把标志为收集参数的参数们打包成一个元组。
不过这里需要注意一下,如果在收集参数后面还需要指定其他参数,那么在调用函数的时候就应该使用关键参数来指定,否则Python就都会把实参都纳入到收集参数中。
举个例子:
建议大家如果定义的函数中带有收集参数,那么可以将其他参数设置为默认参数,例如,print()的原型如下:
print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
objects参数是一个收集参数,如果传入多个参数,将依次打印出来;sep参数指定多个参数之间的分隔符,默认是空格;end参数指定以什么字符结束打印,默认是换行符;file参数指定输出的位置;flush指定是否强制刷新缓存。
在函数的定义中,收集参数前面的星号(*)起到的作用称为“打包”操作,通俗的理解就是将多个参数打包成一个元组的形式进行存储。
在这里给大家介绍一个有趣的技能:星号(*)在形参中的作用是“打包”,而在实参中的作用则相反,起到“解包”的作用。
举个例子:
>>> num = (1, 2, 3, 4, 5) >>> print(num) (1, 2, 3, 4, 5) >>> print(*num) 1 2 3 ?4 5
“解包”操作也适用于其他的序列类型:
>>> name = "FishC" >>> print(*name) F i s h C >>> list1 = [1, 1, 2, 3, 5] >>> print(*list1) 1 1 2 3 5
Python还有另一种收集方式,就是用两个星号(**)表示。与前面的介绍不同,两个星号的收集参数表示为将参数们打包成字典的形式。字典的概念还没有接触,所以在后面讲解字典的章节中再给大家介绍吧。