4.6 函数的定义

我们可以创建一个输出任意范围 Fibonacci 数列的一个函数:

  1. >>> def fib(n): # 写出一直到 n 的 Fibonacci 数列
  2. ... """Print a Fibonacci series up to n."""
  3. ... a, b = 0, 1
  4. ... while a < n:
  5. ... print(a, end=' ')
  6. ... a, b = b, a+b
  7. ... print()
  8. ...
  9. >>> # 现在我们调用刚刚定义的函数:
  10. ... fib(2000)
  11. 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

关键字 def 引入了一个函数的 定义。 def 后面必须跟上函数名和一个用圆括号包围的参数表。组成函数体的语句从下一行开始,而且必须缩进。

函数体的第一句可以选择性的写一个字符串字面量。这个字符串字面量就是函数的 文档字符串 (documentation string, 或者 docstring)。(更多关于文档字符串的内容可以在 文档字符串 章节找到)。有的工具使用文档字符串来自动在线发布或打印文档,或者让用户交互性地浏览代码。在你写的代码中包含文档字符串是一个好习惯,所以养成这个好习惯吧。

函数的 执行 引入了一个新的符号表,这个符号表用于该函数的本地变量。更准确地说,一个函数中,所有变量的分配存放了本地符号表的值。参数的引用会先在本地符号表中查找,然后在封闭函数的本地符号表中查找,然后在全局符号表中查找,最后在内建变量名中查找。因此,全局变量在函数内部不能直接被赋值 (除非在全局语句中命名了),尽管他们可以被引用。

当一个函数被调用的时候,函数调用的实际参数 (实参)会被引入本地符号表中。因此,参数通过 数值调用 来进行传递 (在这里,数值 总是一个对象的 索引,而并不是对象的值)[注释]。当一个函数调用另一函数的时候,一个新的本地符号表就会为这次调用而被创建出来。

本地符号表是啥? 黑人问号???

注释: 实际上,对象引用调用 是一个更好的表述,因为如果传递了一个可变的对象,调用者 (caller) 就能看见被调用函数 (callee) 所引起的所有变化 (列表中插入的元素)。

一个函数的定义引入了在当前符号表中的函数名。函数名的值会被解释器识别为用户自定义函数类型。函数名的值也可以被赋值给另一个名字,这个名字之后也可以当一个函数来用。这就提供了一个一般的重命名机制。

  1. >>> fib
  2. <function fib at 10042ed0>
  3. >>> f = fib
  4. >>> f(100)
  5. 0 1 1 2 3 5 8 13 21 34 55 89

如果你从其他语言过来,你也许会反对说,fib 不是一个函数,而是一个程序,因为它没有返回值。事实上,就算函数没有 return 语句也会 return (返回) 一个数值的,尽管这相当无趣。这个值叫做 None (中文意思就是没有,这是一个内建的名字)。如果 None 是唯一一个写出来的值,一般会被解释器删掉。如果你真的想看见它,你可以使用 print()

  1. >>> fib(0)
  2. >>> print(fib(0))
  3. None

让一个函数直接返回 Fibonacci 数列的一个列表比 print 出来更简单:

  1. >>> def fib2(n): # return Fibonacci series up to n
  2. ... """Return a list containing the Fibonacci series up to n."""
  3. ... result = []
  4. ... a, b = 0, 1
  5. ... while a < n:
  6. ... result.append(a) # see below
  7. ... a, b = b, a+b
  8. ... return result
  9. ...
  10. >>> f100 = fib2(100) # 调用它
  11. >>> f100 # 输出结果
  12. [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

这个例子,就和往常一样,展示了几个 Python 的新特性:

  • return 语句返回一个来自函数的值。return 如果后面不跟表达式参数就会返回 None。离开函数的结尾也会返回 None

  • 表达式 result.append(a) 调用了列表对象 result 的一个 方法 (method)。方法是“属于”一个对象的函数。方法命名为 obj.methodname,这里,obj 是一个对象名 (这里也可以是表达式),methodname 是由该对象类型定义的一个方法的名字。不同的类型定义不同的方法。不同类型可以有相同的方法名而不会造成歧义。(你可以使用 (class) 来定义你自己的对象类型和方法,参考 )。例子中 append() 方法被定义出来用于列表对象。它会在列表的末尾加上一个新的元素。在这个例子中,你使用 result = result + [a] 的结果是一样的,但是 append(a) 更加高效。