4.7. 深入 Python 函数定义

最后更新于:2022-04-01 00:47:42

在 Python 中,你也可以定义包含若干参数的函数。这里有三种可用的形式,也可以混合使用。 ### 4.7.1\. 默认参数值 最常用的一种形式是为一个或多个参数指定默认值。这会创建一个可以使用比定义时允许的参数更少的参数调用的函数,例如: ~~~ def ask_ok(prompt, retries=4, complaint='Yes or no, please!'): 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 IOError('refusenik user') print(complaint) ~~~ 这个函数可以通过几种不同的方式调用: * 只给出必要的参数: 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, only yes or no!') 这个例子还介绍了 in 关键字。它测定序列中是否包含某个确定的值。 默认值在函数 _定义_ 作用域被解析,如下所示: ~~~ i = 5 def f(arg=i): print(arg) i = 6 f() ~~~ 将会输出 5。 **重要警告:** 默认值只被赋值一次。这使得当默认值是可变对象时会有所不同,比如列表、字典或者大多数类的实例。例如,下面的函数在后续调用过程中会累积(前面)传给它的参数: ~~~ def f(a, L=[]): L.append(a) return L print(f(1)) print(f(2)) print(f(3)) ~~~ 这将输出: [1] [1, 2] [1, 2, 3] 如果你不想让默认值在后续调用中累积,你可以像下面一样定义函数: ~~~ def f(a, L=None): if L is None: L = [] L.append(a) return L ~~~ ### 4.7.2\. 关键字参数 函数可以通过 _关键字参数_ 的形式来调用,形如 keyword = 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) # 1 positional argument parrot(voltage=1000) # 1 keyword argument parrot(voltage=1000000, action='VOOOOOM') # 2 keyword arguments parrot(action='VOOOOOM', voltage=1000000) # 2 keyword arguments parrot('a million', 'bereft of life', 'jump') # 3 positional arguments parrot('a thousand', state='pushing up the daisies') # 1 positional, 1 keyword ~~~ 不过以下几种调用是无效的: ~~~ parrot() # required argument missing parrot(voltage=5.0, 'dead') # non-keyword argument after a keyword argument parrot(110, voltage=220) # duplicate value for the same argument parrot(actor='John Cleese') # unknown keyword argument ~~~ 通常,参数列表必须(先书写)位置参数然后才是关键字参数,这里关键字必须来自于形参名字。形参是否有一个默认值并不重要。任何参数都不能被多次赋值——在同一个调用中,与位置参数相同的形参名字不能用作关键字。这里有一个违反此限制而出错的例子: ~~~ >>> def function(a): ... pass ... >>> function(0, a=0) Traceback (most recent call last): File "", line 1, in ? TypeError: function() got multiple values for keyword argument 'a' ~~~ 引入一个形如 **name 的参数时,它接收一个字典(参见 _typesmapping_ ),该字典包含了所有未出现在形式参数列表中的关键字参数。这里可能还会组合使用一个形如 *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) keys = sorted(keywords.keys()) for kw in keys: 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") ~~~ 当然它会按如下内容打印: ~~~ -- Do you have any Limburger ? -- I'm sorry, we're all out of Limburger It's very runny, sir. It's really very, VERY runny, sir. ---------------------------------------- client : John Cleese shopkeeper : Michael Palin sketch : Cheese Shop Sketch ~~~ 注意在打印 关键字 参数字典的内容前先调用 sort() 方法。否则的话,打印参数时的顺序是未定义的。 ### 4.7.3\. 可变参数列表 最后,一个最不常用的选择是可以让函数调用可变个数的参数。这些参数被包装进一个元组(参见[_元组和序列_](http://www.pythondoc.com/pythontutorial3/datastructures.html#tut-tuples) )。在这些可变个数的参数之前,可以有零到多个普通的参数: ~~~ def write_multiple_items(file, separator, *args): file.write(separator.join(args)) ~~~ 通常,这些 可变 参数是参数列表中的最后一个,因为它们将把所有的剩余输入参数传递给函数。任何出现在 *args 后的参数是关键字参数,这意味着,他们只能被用作关键字,而不是位置参数: ~~~ >>> def concat(*args, sep="/"): ... return sep.join(args) ... >>> concat("earth", "mars", "venus") 'earth/mars/venus' >>> concat("earth", "mars", "venus", sep=".") 'earth.mars.venus' ~~~ ### 4.7.4\. 参数列表的分拆 另有一种相反的情况: 当你要传递的参数已经是一个列表,但要调用的函数却接受分开一个个的参数值。这时候你要把已有的列表拆开来。例如内建函数 range() 需要要独立的 _start_,_stop_ 参数。你可以在调用函数时加一个 * 操作符来自动把参数列表拆开: ~~~ >>> list(range(3, 6)) # normal call with separate arguments [3, 4, 5] >>> args = [3, 6] >>> list(range(*args)) # call with arguments unpacked from a list [3, 4, 5] ~~~ 以同样的方式,可以使用 ** 操作符分拆关键字参数为字典: ~~~ >>> def parrot(voltage, state='a stiff', action='voom'): ... print("-- This parrot wouldn't", action, end=' ') ... print("if you put", voltage, "volts through it.", end=' ') ... print("E's", state, "!") ... >>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"} >>> parrot(**d) -- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised ! ~~~ ### 4.7.5\. Lambda 形式 出于实际需要,有几种通常在函数式编程语言例如 Lisp 中出现的功能加入到了 Python。通过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) 43 ~~~ ### 4.7.6\. 文档字符串 这里介绍的文档字符串的概念和格式。 第一行应该是关于对象用途的简介。简短起见,不用明确的陈述对象名或类型,因为它们可以从别的途径了解到(除非这个名字碰巧就是描述这个函数操作的动词)。这一行应该以大写字母开头,以句号结尾。 如果文档字符串有多行,第二行应该空出来,与接下来的详细描述明确分隔。接下来的文档应该有一或多段描述对象的调用约定、边界效应等。 Python 的解释器不会从多行的文档字符串中去除缩进,所以必要的时候应当自己清除缩进。这符合通常的习惯。第一行之后的第一个非空行决定了整个文档的缩进格式。(我们不用第一行是因为它通常紧靠着起始的引号,缩进格式显示的不清楚。)留白“相当于”是字符串的起始缩进。每一行都不应该有缩进,如果有缩进的话,所有的留白都应该清除掉。留白的长度应当等于扩展制表符的宽度(通常是8个空格)。 以下是一个多行文档字符串的示例: ~~~ >>> def my_function(): ... """Do nothing, but document it. ... ... No, really, it doesn't do anything. ... """ ... pass ... >>> print(my_function.__doc__) Do nothing, but document it. No, really, it doesn't do anything. ~~~
';