4.6 函数的定义
我们可以创建一个输出任意范围 Fibonacci 数列的一个函数:
>>> def fib(n): # 写出一直到 n 的 Fibonacci 数列
... """Print a Fibonacci series up to n."""
... a, b = 0, 1
... while a < n:
... print(a, end=' ')
... a, b = b, a+b
... print()
...
>>> # 现在我们调用刚刚定义的函数:
... fib(2000)
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) 所引起的所有变化 (列表中插入的元素)。
一个函数的定义引入了在当前符号表中的函数名。函数名的值会被解释器识别为用户自定义函数类型。函数名的值也可以被赋值给另一个名字,这个名字之后也可以当一个函数来用。这就提供了一个一般的重命名机制。
>>> fib
<function fib at 10042ed0>
>>> f = fib
>>> f(100)
0 1 1 2 3 5 8 13 21 34 55 89
如果你从其他语言过来,你也许会反对说,fib
不是一个函数,而是一个程序,因为它没有返回值。事实上,就算函数没有 return 语句也会 return (返回) 一个数值的,尽管这相当无趣。这个值叫做 None
(中文意思就是没有,这是一个内建的名字)。如果 None
是唯一一个写出来的值,一般会被解释器删掉。如果你真的想看见它,你可以使用 print()
:
>>> fib(0)
>>> print(fib(0))
None
让一个函数直接返回 Fibonacci 数列的一个列表比 print 出来更简单:
>>> def fib2(n): # return Fibonacci series up to n
... """Return a list containing the Fibonacci series up to n."""
... result = []
... a, b = 0, 1
... while a < n:
... result.append(a) # see below
... a, b = b, a+b
... return result
...
>>> f100 = fib2(100) # 调用它
>>> f100 # 输出结果
[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)
更加高效。