• 4.6. 定义函数

    4.6. 定义函数

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

    1. >>> def fib(n): # write Fibonacci series up to n
    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. >>> # Now call the function we just defined:
    10. ... fib(2000)
    11. 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

    关键字 def 引入一个函数 定义。它必须后跟函数名称和带括号的形式参数列表。构成函数体的语句从下一行开始,并且必须缩进。

    函数体的第一个语句可以(可选的)是字符串文字;这个字符串文字是函数的文档字符串或 docstring 。(有关文档字符串的更多信息,请参阅 文档字符串 部分)有些工具使用文档字符串自动生成在线或印刷文档,或者让用户以交互式的形式浏览代码;在你编写的代码中包含文档字符串是一种很好的做法,所以要养成习惯。

    函数的 执行 会引入一个用于函数局部变量的新符号表。 更确切地说,函数中所有的变量赋值都将存储在局部符号表中;而变量引用会首先在局部符号表中查找,然后是外层函数的局部符号表,最后是内置名称表。 因此,全局变量和外层函数的变量不能在函数内部直接赋值(除非是在 global 语句中定义的全局变量,或者是在 nonlocal 语句中定义的外层函数的变量),尽管它们可以被引用。

    在函数被调用时,实际参数(实参)会被引入被调用函数的本地符号表中;因此,实参是通过 按值调用 传递的(其中 始终是对象 引用 而不是对象的值)。1 当一个函数调用另外一个函数时,将会为该调用创建一个新的本地符号表。

    函数定义会把函数名引入当前的符号表中。函数名称的值具有解释器将其识别为用户定义函数的类型。这个值可以分配给另一个名称,该名称也可以作为一个函数使用。这用作一般的重命名机制:

    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 语句的函数也会返回一个值,尽管它是一个相当无聊的值。这个值称为 None (它是内置名称)。一般来说解释器不会打印出单独的返回值 None ,如果你真想看到它,你可以使用 print()

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

    写一个返回斐波那契数列的列表,而不是打印出来的函数,非常简单:

    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) # call it
    11. >>> f100 # write the result
    12. [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

    此示例中,像往常一样,演示了一些新的 Python 功能:

    • return 语句会从函数内部返回一个值。 不带表达式参数的 return 会返回 None。 函数执行完毕退出也会返回 None

    • result.append(a) 语句调用了列表对象 result方法 。方法是“属于”一个对象的函数,它被命名为 obj.methodname ,其中 obj 是某个对象(也可能是一个表达式), methodname 是由对象类型中定义的方法的名称。不同的类型可以定义不同的方法。不同类型的方法可以有相同的名称而不会引起歧义。(可以使用 定义自己的对象类型和方法,请参阅 类 )示例中的方法 append() 是为列表对象定义的;它会在列表的最后添加一个新的元素。在这个示例中它相当于 result = result + [a] ,但更高效。