1.4 设计函数
无论程序规模大小,函数都是其核心组成部分,也是我们在编程语言中表达计算过程的主要媒介。到目前为止,我们已经讨论了函数的形式特性以及它们如何被调用。现在,我们将话题转到“什么是优秀的函数”。从根本上说,优秀函数的所有品质都在强化一个核心理念:函数即抽象。
- 每个函数应该只做一件事。这项工作应该能用一个简短的名称来概括,并能用一行文字描述其特征。如果一个函数按顺序执行多项任务,则应将其拆分为多个函数
- 不要重复自己(Don't Repeat Yourself)。这是软件工程的一个核心信条。所谓的 DRY 原则指出,多段代码不应描述冗余的逻辑。相反,逻辑应该只实现一次,赋予其名称,然后多次应用。如果你发现自己在复制粘贴一段代码,那么你可能已经找到了一个进行函数抽象的机会
- 函数的定义应具有通用性。比如,作为
pow函数的一个特例,平方函数就不在 Python 库中,因为pow函数可以计算任意幂次。
这些准则能提高代码的可读性,减少错误,并通常能减少编写的代码总量。将复杂任务分解为简洁的函数是一项需要经验才能掌握的技能。幸运的是,Python 提供了多项特性来支持你的努力。
1.4.1 文档
函数定义通常会包含描述该函数的文档,称为文档字符串(docstring)。它必须与函数体一起缩进。按照惯例,文档字符串通常使用三引号括起来。第一行用一句话描述函数的功能,随后的段落可以详细说明参数并澄清函数的行为:
>>> def pressure(v, t, n):
"""计算理想气体的压力(单位:帕斯卡)
使用理想气体定律:http://en.wikipedia.org/wiki/Ideal_gas_law
v -- 气体体积,单位:立方米
t -- 绝对温度,单位:开尔文
n -- 气体粒子数
"""
k = 1.38e-23 # 玻尔兹曼常数
return n * k * t / v当你以函数名作为参数调用 help 函数时,就可以看到它的文档字符串(在 Python 帮助界面输入 q 退出):
>>> help(pressure)编写 Python 程序时,除了最简单的函数外,应为所有函数编写文档字符串。请记住:代码只写一次,但会被阅读多次。Python 文档包含一套 文档字符串准则,用于保持不同 Python 项目之间的一致性。
注释:Python 中的注释可以附在行尾,紧跟在 # 符号后面。例如,上面的注释“玻尔兹曼常数”描述了变量 k。这些注释永远不会出现在 Python 的 help 信息中,也会被解释器忽略。它们仅供人类阅读。
1.4.2 默认参数值
定义通用函数的一个后果是会引入额外的参数。参数过多的函数调用起来可能很别扭,且难以阅读。
在 Python 中,我们可以为函数的参数提供默认值。调用该函数时,带有默认值的参数是可选的。如果没有提供该参数,则默认值将绑定到相应的形参名上。例如,如果一个应用经常计算一摩尔粒子的压强,可以将这个值设为默认值:
>>> def pressure(v, t, n=6.022e23):
"""计算理想气体的压强(单位:帕斯卡)。
v -- 气体体积,单位:立方米
t -- 绝对温度,单位:开尔文
n -- 气体粒子数(默认值:一摩尔)
"""
k = 1.38e-23 # 玻尔兹曼常数
return n * k * t / v在这个例子中,= 符号根据其使用的语境有两种不同的含义。在 def 语句的标题(header)中,= 不执行赋值操作,而是表示调用 pressure 函数时使用的默认值。相比之下,函数体内部对 k 的赋值语句则是将名称 k 绑定到玻尔兹曼常数的近似值上。
>>> pressure(1, 273.15)
2269.974834
>>> pressure(1, 273.15, 3 * 6.022e23)
6809.924502pressure 函数定义为接受三个参数,但在上面的第一个调用表达式中只提供了两个。在这种情况下,n 的值取自 def 语句中的默认值。如果提供了第三个参数,则默认值会被忽略。
作为一条准则,函数体中用到的大多数数据都应表达为形参的默认值,以便于查看,也方便函数调用者进行更改。至于一些永远不会改变的值(如基础常数 k),则可以绑定在函数体内或全局帧中。