定义函数
我们通过斐波那契数列来理解定义函数
>>> def fib(n): # 将斐波那契数列打印到 n ... """将斐波那契数列打印到 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 引入了一个函数定义 。其后面必须跟随有函数的名称以及用括号包起来的一系列参数。构成函数体的语句从下一行开始,并且必须缩进。
函数体的第一个语句可以是一个字符串常量,这个字符串常量就是这个函数的文档字符串,或者说是 docstring ,有很多工具可以用于在线或者可打印文档的自动化生成,或者可以让用户交互地在代码中浏览文档;在代码中写文档字符串是比较好的实践,所以请养成写文档字符串的习惯。
函数的执行引入了一个新的符号表用于存储函数的局部变量。更准确地说,在函数内的所有变量赋值都会被存储到这张局部符号表中;所以在查找一个变量的引用时,会先查找局部符号表,然后查找闭包函数的局部符号表,接着是全局符号表,最后才是内置名称表。因此,尽管可能在函数中引用全局变量,但在函数中无法对全局变量直接进行赋值(除非用 global 语句来定义一个变量)
当一个函数被调用时,函数参数被引入到局部符号表中;因此,参数是通过 按值传递 的方式来传递的(这个值表示一个对象的 引用 ,而不是该对象的值)。[1] 当在一个函数中调用另外一个函数时,将会为这次调用创建一个新的局部符号表。
一个函数定义将会在当前符号表中引入函数的名称。这个函数的名称对应的值的类型会被解释器解释为用户定义的函数。这个值可以被赋值给另外一个名称,并且将这名称可以当作一个函数来使用。这是一种常用的重命名机制:
>>> fib <function fib at 10042ed0> >>> f = fib >>> f(100) 0 1 1 2 3 5 8 13 21 34 55 89
默认参数值
最常用的形式是为一个或多个参数指定默认值。这样,函数可以以少于其定义的参数被调用。比如:
def ask_ok(prompt, retries=4, reminder='Please try again!'): while True: ok = input(prompt) if ok in ('y', 'ye', 'yes'): return True if ok in ('n', 'no', 'nop', 'nope'): return False retries = retries - 1 if retries < 0: raise ValueError('invalid user response') print(reminder)
该函数可以有几种不同的调用方式:
只指定强制的参数
参数: ask_ok('Do you really want to quit?')
提供一个可选参数
参数: ask_ok('OK to overwrite the file?', 2)
或者给定全部的参数
参数: ask_ok('OK to overwrite the file?', 2, 'Come on, onlyyes or no!')
上述例子顺便也提及了 in 关键字。它是用来测试某个特定值是否在一个序列中。
默认值是在定义函数时的 “定义过程中” (defining )的范围内评估的(函数参数默认值是个变量的话,要根据函数定义前变量的值来确定参数默认值), 所以,
i = 5 def f(arg=i): print(arg) i = 6 f()会打印 5.
关键字参数
形如 kwarg=value 形式的参数是 关键字参数。例如,以下函数:
def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'): print("-- This parrot wouldn't", action, end=' ') print("if you put", voltage, "volts through it.") print("-- Lovely plumage, the", type) print("-- It's", state, "!")
接收一个必选参数 (voltage ) 和三个可选参数( state,action, 和 type )。这个函数下方式调用:
parrot(1000) # 一个位置参数 parrot(voltage=1000) # 一个关键字参数 parrot(voltage=1000000, action='VOOOOOM') # 2个关键字参数 parrot(action='VOOOOOM', voltage=1000000) # 2个关键字参数 parrot('a million', 'bereft of life', 'jump') # 3个位置参数 parrot('a thousand', state='pushing up the daisies') # 一个位置参数,一个关键字参数
但是下列的所有调用方式都是无效的:
parrot() # 必选参数缺失 parrot(voltage=5.0, 'dead') # 非关键字参数在关键字参数后面 parrot(110, voltage=220) # 同一参数重复赋值 parrot(actor='John Cleese') # 未知关键字参数
在函数调用中,关键字参数必须遵循参数位置。传递的所有关键字参数必须跟函数接受的其中一个参数相匹配。(例如: actor 在函数 parrot 中不是一个有效的参数),并且它们的顺序并不重要。这同样也包括那些非必选参数 (例如 parrot(voltage=1000) 同样有效)。没有参数可能多次获取一个值。下例就是因此而失败的:
>>> def function(a): ... pass ... >>> function(0, a=0) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: function() got multiple values for keyword argument 'a'
当最后存在 **name 形式的参数时,它最后会接收一个字典,(参见 Mapping Types --- dict) 包含所有除了和形式参数相对应的关键字参数。这可以与 * name 形式的形式参数(在下一小节中描述)结合,该参数接收包含正式参数列表之外的位置参数的元组。 (*name 必须出现在 **name 之前。) 例如,我们如果定义一个如下函数:
def cheeseshop(kind, *arguments, **keywords): print("-- Do you have any", kind, "?") print("-- I'm sorry, we're all out of", kind) for arg in arguments: print(arg) print("-" * 40) for kw in keywords: print(kw, ":", keywords[kw])
它可以像这样调用:
cheeseshop("Limburger", "It's very runny, sir.", "It's really very, VERY runny, sir.", shopkeeper="Michael Palin", client="John Cleese", sketch="Cheese Shop Sketch")
可变参数
最后,最不常用的指定参数的选项是可变数量的参数。这些参数将被组装成一个元组 (参见 元组和序列) 。在可变参数之前,可能会出现零个或多个正常参数。
def write_multiple_items(file, separator, *args): file.write(separator.join(args))
分离参数列表
当输入的参数已经是列表或元组形式而为了调用其中单独的位置参数时,将会出现与上面相反的情况。例如内置函数 range() 需要有独立的 start 和 stop 参数。如果输入的时候不是独立的参数,则需要用 * 操作符来将参数从列表或者元组里面提取出来:
>>> list(range(3, 6)) # 正常利用参数调用函数 [3, 4, 5] >>> args = [3, 6] >>> list(range(*args)) # 从列表中提取参数来调用函数 [3, 4, 5]
Lambda 表达式
我们可以使用 lambda关键字来创建小型匿名函数。此函数会返回其两个参数的和:lambda a,b:a + b。可以在任何需要函数对象的场合使用 Lambda 函数。它们在语法上仅限于单个表达式。从语义上讲,它们只是普通函数定义的语法糖。与嵌套函数定义类似,lambda 函数可以从包含它的上下文中引用变量:
>>> def make_incrementor(n): ... return lambda x: x + n ... >>> f = make_incrementor(42) >>> f(0) 42 >>> f(1) 4