| 关键词: 函数 形参 参数 可以 语句 定义 关键字 print 对象 位置 |
4. 其他流程控制工具 除了上一章介绍的 while 语句,Python 还支持其他语言中常见的流程控制语句,只是稍有不同。 4.1. if 语句 最让人耳熟能详的应该是 if 语句。例如: >>> >>> x = int(input("Please enter an integer: ")) Please enter an integer: 42 >>> if x < 0: ... x = 0 ... print('Negative changed to zero') ... elif x == 0: ... print('Zero') ... elif x == 1: ... print('Single') ... else: ... print('More') ... More if 语句包含零个或多个 elif 子句,及可选的 else 子句。关键字 'elif' 是 'else if' 的缩写,适用于避免过多的缩进。可以把 if ... elif ... elif ... 序列看作是其他语言中 switch 或 case 语句的替代品。 4.2. for 语句 Python 的 for 语句与 C 或 Pascal 中的不同。Python 的 for 语句不迭代算术递增数值(如 Pascal),或是给予用户定义迭代步骤和暂停条件的能力(如 C),而是迭代列表或字符串等任意序列,元素的迭代顺序与在序列中出现的顺序一致。 例如: >>> >>> # Measure some strings: ... words = ['cat', 'window', 'defenestrate'] >>> for w in words: ... print(w, len(w)) ... cat 3 window 6 defenestrate 12 遍历某个集合的同时修改该集合的内容,很难获取想要的结果。要在遍历时修改集合的内容,应该遍历该集合的副本或创建新的集合: # Strategy: Iterate over a copy for user, status in users.copy().items(): if status == 'inactive': del users[user] # Strategy: Create a new collection active_users = {} for user, status in users.items(): if status == 'active': active_users[user] = status 4.3. range() 函数 内置函数 range() 常用于遍历数字序列,该函数可以生成算术级数: >>> >>> for i in range(5): ... print(i) ... 0 1 2 3 4 生成的序列不包含给定的终止数值;range(10) 生成 10 个值,这是一个长度为 10 的序列,其中的元素索引都是合法的。range 可以不从 0 开始,还可以按指定幅度递增(递增幅度称为 '步进',支持负数): >>> >>> list(range(5, 10)) [5, 6, 7, 8, 9] >>> list(range(0, 10, 3)) [0, 3, 6, 9] >>> list(range(-10, -100, -30)) [-10, -40, -70] range() 和 len() 组合在一起,可以按索引迭代序列: >>> >>> a = ['Mary', 'had', 'a', 'little', 'lamb'] >>> for i in range(len(a)): ... print(i, a) ... 0 Mary 1 had 2 a 3 little 4 lamb 不过,大多数情况下,enumerate() 函数更便捷,详见 循环的技巧 。 如果只输出 range,会出现意想不到的结果: >>> >>> range(10) range(0, 10) range() 返回对象的操作和列表很像,但其实这两种对象不是一回事。迭代时,该对象基于所需序列返回连续项,并没有生成真正的列表,从而节省了空间。 这种对象称为可迭代对象 iterable,函数或程序结构可通过该对象获取连续项,直到所有元素全部迭代完毕。for 语句就是这样的架构,sum() 是一种把可迭代对象作为参数的函数: >>> >>> sum(range(4)) # 0 + 1 + 2 + 3 6 下文将介绍更多返回可迭代对象或把可迭代对象当作参数的函数。 在 数据结构 这一章节中,我们将讨论有关 list() 的更多细节。 4.4. 循环中的 break、continue 语句及 else 子句 break 语句和 C 中的类似,用于跳出最近的 for 或 while 循环。 循环语句支持 else 子句;for 循环中,可迭代对象中的元素全部循环完毕时,或 while 循环的条件为假时,执行该子句;break 语句终止循环时,不执行该子句。 请看下面这个查找素数的循环示例: >>> >>> for n in range(2, 10): ... for x in range(2, n): ... if n % x == 0: ... print(n, 'equals', x, '*', n//x) ... break ... else: ... # loop fell through without finding a factor ... print(n, 'is a prime number') ... 2 is a prime number 3 is a prime number 4 equals 2 * 2 5 is a prime number 6 equals 2 * 3 7 is a prime number 8 equals 2 * 4 9 equals 3 * 3 (没错,这段代码就是这么写。仔细看:else 子句属于 for 循环,不属于 if 语句。) 与 if 语句相比,循环的 else 子句更像 try 的 else 子句: try 的 else 子句在未触发异常时执行,循环的 else 子句则在未运行 break 时执行。try 语句和异常详见 处理异常。 continue 语句也借鉴自 C 语言,表示继续执行循环的下一次迭代: >>> >>> for num in range(2, 10): ... if num % 2 == 0: ... print("Found an even number", num) ... continue ... print("Found an odd number", num) ... Found an even number 2 Found an odd number 3 Found an even number 4 Found an odd number 5 Found an even number 6 Found an odd number 7 Found an even number 8 Found an odd number 9 4.5. pass 语句 pass 语句不执行任何操作。语法上需要一个语句,但程序不实际执行任何动作时,可以使用该语句。例如: >>> >>> while True: ... pass # Busy-wait for keyboard interrupt (Ctrl+C) ... 下面这段代码创建了一个最小的类: >>> >>> class MyEmptyClass: ... pass ... pass 还可以用作函数或条件子句的占位符,让开发者聚焦更抽象的层次。此时,程序直接忽略 pass: >>> >>> def initlog(*args): ... pass # Remember to implement this! ... 4.6. 定义函数 下列代码创建一个可以输出限定数值内的斐波那契数列函数: >>> >>> 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,详见 文档字符串。利用文档字符串可以自动生成在线文档或打印版文档,还可以让开发者在浏览代码时直接查阅文档;Python 开发者最好养成在代码中加入文档字符串的好习惯。 函数在 执行 时使用函数局部变量符号表,所有函数变量赋值都存在局部符号表中;引用变量时,首先,在局部符号表里查找变量,然后,是外层函数局部符号表,再是全局符号表,最后是内置名称符号表。因此,尽管可以引用全局变量和外层函数的变量,但最好不要在函数内直接赋值(除非是 global 语句定义的全局变量,或 nonlocal 语句定义的外层函数变量)。 在调用函数时会将实际参数(实参)引入到被调用函数的局部符号表中;因此,实参是使用 按值调用 来传递的(其中的 值 始终是对象的 引用 而不是对象的值)。 1 当一个函数调用另外一个函数时,会为该调用创建一个新的局部符号表。 函数定义在当前符号表中把函数名与函数对象关联在一起。解释器把函数名指向的对象作为用户自定义函数。还可以使用其他名称指向同一个函数对象,并访问访该函数: >>> >>> fib <function fib at 10042ed0> >>> f = fib >>> f(100) 0 1 1 2 3 5 8 13 21 34 55 89 fib 不返回值,因此,其他语言不把它当作函数,而是当作过程。事实上,没有 return 语句的函数也返回值,只不过这个值比较是 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(a) 语句调用了列表对象 result 的 方法 。方法是“从属于”对象的函数,命名为 obj.methodname,obj 是对象(也可以是表达式),methodname 是对象类型定义的方法名。不同类型定义不同的方法,不同类型的方法名可以相同,且不会引起歧义。(用 类 可以自定义对象类型和方法,详见 类 )示例中的方法 append() 是为列表对象定义的,用于在列表末尾添加新元素。本例中,该方法相当于 result = result + [a] ,但更有效。 4.7. 函数定义详解 函数定义支持可变数量的参数。这里列出三种可以组合使用的形式。 4.7.1. 默认值参数 为参数指定默认值是非常有用的方式。调用函数时,可以使用比定义时更少的参数,例如: 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, 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. 关键字参数 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) # 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 函数调用时,关键字参数必须跟在位置参数后面。所有传递的关键字参数都必须匹配一个函数接受的参数(比如,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 形式时,接收一个字典(详见 映射类型 --- dict),该字典包含与函数中已定义形参对应之外的所有关键字参数。**name 形参可以与 *name 形参(下一小节介绍)组合使用(*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") |
|
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除
|