4.6. 定义函数
最后更新于:2022-04-01 00:47:39
我们可以创建一个用来生成指定边界的斐波那契数列的函数:
~~~
>>> def fib(n): # write Fibonacci series up to n
... """Print a Fibonacci series up to n."""
... a, b = 0, 1
... while a n:
... print(a, end=' ')
... a, b = b, a+b
... print()
...
>>> # Now call the function we just defined:
... fib(2000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597
~~~
关键字 def 引入了一个函数 _定义_。在其后必须跟有函数名和包括形式参数的圆括号。函数体语句从下一行开始,必须是缩进的。
函数体的第一行语句可以是可选的字符串文本,这个字符串是函数的文档字符串,或者称为_docstring_。(更多关于 docstrings 的信息请参考 [_文档字符串_](http://www.pythondoc.com/pythontutorial3/controlflow.html#tut-docstrings)) 有些工具通过 docstrings 自动生成在线的或可打印的文档,或者让用户通过代码交互浏览;在你的代码中包含 docstrings 是一个好的实践,让它成为习惯吧。
函数 _调用_ 会为函数局部变量生成一个新的符号表。确切的说,所有函数中的变量赋值都是将值存储在局部符号表。变量引用首先在局部符号表中查找,然后是包含函数的局部符号表,然后是全局符号表,最后是内置名字表。因此,全局变量不能在函数中直接赋值(除非用 global 语句命名),尽管他们可以被引用。
函数引用的实际参数在函数调用时引入局部符号表,因此,实参总是 _传值调用_ (这里的 _值_ 总是一个对象 引用 ,而不是该对象的值)。[[1]](http://www.pythondoc.com/pythontutorial3/controlflow.html#id10) 一个函数被另一个函数调用时,一个新的局部符号表在调用过程中被创建。
一个函数定义会在当前符号表内引入函数名。函数名指代的值(即函数体)有一个被 Python 解释器认定为 _用户自定义函数_ 的类型。 这个值可以赋予其他的名字(即变量名),然后它也可以被当做函数使用。这可以作为通用的重命名机制:
~~~
>>> fib
>>> f = fib
>>> f(100)
0 1 1 2 3 5 8 13 21 34 55 89
~~~
如果你使用过其他语言,你可能会反对说:fib 不是一个函数,而是一个方法,因为它并不返回任何值。事实上,没有 return 语句的函数确实会返回一个值,虽然是一个相当令人厌烦的值(指 None )。这个值被称为 None (这是一个内建名称)。如果 None 值是唯一被书写的值,那么在写的时候通常会被解释器忽略(即不输出任何内容)。如果你确实想看到这个值的输出内容,请使用print() 函数:
~~~
>>> fib(0)
>>> print(fib(0))
None
~~~
定义一个返回斐波那契数列数字列表的函数,而不是打印它,是很简单的:
~~~
>>> 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) # call it
>>> f100 # write the result
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
~~~
和以前一样,这个例子演示了一些新的 Python 功能:
* return 语句从函数中返回一个值,不带表达式的 return 返回 None。过程结束后也会返回None。
* 语句 result.append(b) 称为链表对象 result 的一个 _方法_。方法是一个“属于”某个对象的函数,它被命名为 obj.methodename,这里的 obj 是某个对象(可能是一个表达式), methodename 是某个在该对象类型定义中的方法的命名。不同的类型定义不同的方法。不同类型可能有同样名字的方法,但不会混淆。(当你定义自己的对象类型和方法时,可能会出现这种情况,_class_ 的定义方法详见 [_类_](http://www.pythondoc.com/pythontutorial3/classes.html#tut-classes) )。示例中演示的 append() 方法由链表对象定义,它向链表中加入一个新元素。在示例中它等同于 result = result + [b],不过效率更高。