Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 24 additions & 26 deletions sicp/1/4.md
Original file line number Diff line number Diff line change
@@ -1,70 +1,68 @@
# 1.4 设计函数

::: details INFO
译者:[Mancuoj](https://github.com/mancuoj)
译者:[mancuoj](https://github.com/mancuoj)、[clcs](https://github.com/clcs)

来源:[1.4 Designing Functions](https://www.composingprograms.com/pages/14-designing-functions.html)

对应:Lab 01
:::

函数是所有程序(无论大小)的基本组成部分,并且是我们使用编程语言来表达计算过程的主要媒介。之前我们已经讨论过了函数的形式及其调用方式,而本节我们将讨论“什么是一个好函数”。从根本上说,好函数共有的品质就是:它们都强化了“函数就是抽象”的理念
无论程序规模大小,**函数**都是其核心组成部分,也是我们在编程语言中表达计算过程的**主要媒介**。到目前为止,我们已经讨论了函数的形式特性以及它们如何被调用。现在,我们将话题转到“什么是优秀的函数”。从根本上说,优秀函数的所有品质都在强化一个核心理念:**函数即抽象**

- 每个函数应该只负责一个任务,且该任务要用一个简短的名称来识别,并在一行文本中进行描述。按顺序执行多个任务的函数应该分为多个函数。
- 不要重复自己(Don't repeat yourself)是软件工程的核心原则。这个所谓的 DRY 原则指出,多个代码片段不应该描述重复的逻辑。相反,逻辑应该只实现一次,为其指定一个名称后多次使用。如果你发现自己正在复制粘贴一段代码,那么你可能已经找到了进行函数抽象的机会。
- 定义通用的函数。比如作为 `pow` 函数的一个特例的平方函数就不在 Python 库中,因为 `pow` 函数可以将数字计算为任意次方
- **每个函数应该只做一件事**。这项工作应该能用一个简短的名称来概括,并能用一行文字描述其特征。如果一个函数按顺序执行多项任务,则应将其拆分为多个函数
- **不要重复自己**(Don't Repeat Yourself)。这是软件工程的一个核心信条。所谓的 **DRY 原则**指出,多段代码不应描述冗余的逻辑。相反,逻辑应该只实现一次,赋予其名称,然后多次应用。如果你发现自己在复制粘贴一段代码,那么你可能已经找到了一个进行函数抽象的机会
- **函数的定义应具有通用性**。比如,作为 `pow` 函数的一个**特例**,平方函数就不在 Python 库中,因为 `pow` 函数可以计算任意幂次

这些准则提高了代码的可读性,减少了错误的数量,并且通常最大限度地减少了编写的代码总量。将复杂的任务分解为简洁的功能是一项需要经验才能掌握的技能。幸运的是,Python 提供了多种特性来支持你的工作
这些准则能提高代码的可读性,减少错误,并通常能减少编写的代码总量。将复杂任务分解为简洁的函数是一项需要经验才能掌握的技能。幸运的是,Python 提供了多项特性来支持你的努力

## 1.4.1 文档

函数定义通常包括描述函数的文档,称为文档字符串 docstring”,它必须在函数体中缩进。文档字符串通常使用三个引号,第一行描述函数的任务,随后的几行可以描述参数并解释函数的意图
函数定义通常会包含描述该函数的文档,称为**文档字符串**(docstring)。它必须与函数体**一起缩进**。按照惯例,文档字符串通常使用三引号括起来。第一行用一句话描述函数的功能,随后的段落可以详细说明参数并澄清函数的行为

```py
>>> def pressure(v, t, n):
"""计算理想气体的压力,单位为帕斯卡
"""计算理想气体的压力(单位:帕斯卡)

使用理想气体定律:http://en.wikipedia.org/wiki/Ideal_gas_law

v -- 气体体积,单位为立方米
t -- 绝对温度,单位为开尔文
n -- 气体粒子
v -- 气体体积,单位:立方米
t -- 绝对温度,单位:开尔文
n -- 气体粒子数
"""
k = 1.38e-23 # 玻尔兹曼常数
return n * k * t / v
```

当你使用函数名称作为参数调用 `help` 时,你会看到它的文档字符串(键入 q 以退出 Python help)。
当你以函数名作为参数调用 `help` 函数时,就可以看到它的文档字符串(在 Python 帮助界面输入 `q` 退出):

```py
>>> help(pressure)
```

编写 Python 程序时,除了最简单的函数之外,都要包含文档字符串。要记住,虽然代码只编写一次,但是会在之后阅读多次。Python 文档包含了 [文档字符串准则](http://www.python.org/dev/peps/pep-0257/),它会在不同的 Python 项目中保持一致
编写 Python 程序时,除了最简单的函数外,应为所有函数编写文档字符串。请记住:代码只写一次,但会被阅读多次。Python 文档包含一套 [文档字符串准则](http://www.python.org/dev/peps/pep-0257/),用于保持不同 Python 项目之间的一致性

注释:Python 中的注释可以附加到 `#` 号后的行尾。例如,上面代码中的注释 `玻尔兹曼常数` 描述了 `k` 变量的含义。这些注释不会出现在 Python 的 `help` 中,而且会被解释器忽略,它们只为人类而存在
**注释**:Python 中的注释可以附在行尾,紧跟在 `#` 符号后面。例如,上面的注释“玻尔兹曼常数”描述了变量 `k`。这些注释永远不会出现在 Python 的 `help` 信息中,也会被解释器忽略。它们**仅供人类阅读**

## 1.4.2 参数默认值
## 1.4.2 默认参数值

定义通用函数的结果是引入了额外的参数。具有许多参数的函数可能调用起来很麻烦并且难以阅读
定义通用函数的一个后果是会引入额外的参数。参数过多的函数调用起来可能很别扭,且难以阅读

在 Python 中,我们可以为函数的参数提供默认值。当调用该函数时,具有默认值的参数是可选的。如果未提供,则将默认值绑定到形参上。例如,如果程序通常用于计算“一摩尔”粒子的压力,则可以提供此值作为默认值
在 Python 中,我们可以为函数的参数提供**默认值**。调用该函数时,带有默认值的参数是**可选**的。如果没有提供该参数,则默认值将绑定到相应的形参名上。例如,如果一个应用经常计算一摩尔粒子的压强,可以将这个值设为默认值

```py
>>> def pressure(v, t, n=6.022e23):
"""计算理想气体的压力,单位为帕斯卡
"""计算理想气体的压强(单位:帕斯卡)。

使用理想气体定律:http://en.wikipedia.org/wiki/Ideal_gas_law

v -- 气体体积,单位为立方米
t -- 绝对温度,单位为开尔文
n -- 气体粒子,默认为一摩尔
v -- 气体体积,单位:立方米
t -- 绝对温度,单位:开尔文
n -- 气体粒子数(默认值:一摩尔)
"""
k = 1.38e-23 # 玻尔兹曼常数
return n * k * t / v
```

`=` 符号在此示例中表示两种不同的含义,具体取决于使用它的上下文。在 def 语句中,`=` 不执行赋值,而是指示调用 `pressure` 函数时使用的默认值。相比之下,函数体中对 `k` 的赋值语句中将名称 `k` 与玻尔兹曼常数的近似值进行了绑定
在这个例子中,`=` 符号根据其使用的语境有两种不同的含义。在 `def` 语句的标题(header)中,`=` 不执行赋值操作,而是表示调用 `pressure` 函数时使用的默认值。相比之下,函数体内部对 `k` 的赋值语句则是将名称 `k` 绑定到玻尔兹曼常数的近似值上

```py
>>> pressure(1, 273.15)
Expand All @@ -73,6 +71,6 @@
6809.924502
```

`pressure` 函数的定义接收三个参数,但上面的第一个调用表达式中只提供了两个。在这种情况下,`n` 的值取自 `def` 语句中的默认值。如果提供了第三个参数,默认值将被忽略
`pressure` 函数定义为接受三个参数,但在上面的第一个调用表达式中只提供了两个。在这种情况下,`n` 的值取自 `def` 语句中的默认值。如果提供了第三个参数,则默认值会被忽略

作为准则,函数主体中使用的大多数数据值都应该表示为具名参数(named arguments)的默认值,这样会使它们更易于检查,并且可以被函数调用者更改。一些永远不会改变的值,例如基本常量 `k` 可以绑定在函数体或全局帧中
作为一条准则,函数体中用到的大多数数据都应表达为形参的默认值,以便于查看,也方便函数调用者进行更改。至于一些永远不会改变的值(如基础常数 `k`),则可以绑定在函数体内或全局帧中
Loading