4. 其它流程控制工具
除了前面介绍的 while 语句,Python 还从别的语言中借鉴了一些流程控制功能,并有所改变。
4.1 if 语句
也许最有句的语句类型是 if 语句。例如:
>;>;>; x = int(raw_input("Please enter an integer: "))
>;>;>; if x <0:
... x = 0
... print 'Negative changed to zero'
... elif x == 0:
... print 'Zero'
... elif x == 1:
... print 'Single'
... else:
... print 'More'
...
可能会有 0 或很多个 elif 部分,else 是可选的。关键字“elif ”是“ else if ”的缩写,这个可以有效避免过深的缩进。if ... elif ... elif ... 序列用于替代其它语言中的 switch 或 case 语句。
4.3 range() 函数
如果你需要一个数值序列,内置函数 range()可能会很有用,它生成一个等差级数链表。
>;>;>; range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
range(10)生成了一个包含10个值的 链表,它准确的用链表的索引值填充了这个长度为10的列表,所生成的链表中不包括范围中的结束值。也可以让range操作从另一个数值开始, 或者可以指定一个不同的步进值(甚至是负数,有时这也被称为“步长”):
>;>;>; range(5, 10)
[5, 6, 7, 8, 9]
>;>;>; range(0, 10, 3)
[0, 3, 6, 9]
>;>;>; 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
4.4 break 和 continue 语句,以及循环中的 else 子句
break语句和C中的类似,用于跳出最近的一级for或while循环。
continue 语句是 从C中借鉴来的,它表示循环继续执行下一次迭代。
循环可以有一个else子句;它在循环迭代完整个列表(对于for)或执行条件为false(对于 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
4.5 pass 语句
pass 语句什么也不做。它用于那些 语法上必须要有什么语句,但程序上什么也不要做的场合,例如:
>;>;>; while True:
... pass # Busy-wait for keyboard interrupt
. ..
4.6 定义函数
我们可以编写一个函数来生成有给定上界的菲波那契数列:
>;>;>; def fib(n): # write Fibonacci series up to n
. .. """Print a Fibonacci series up to n."""
... a, b = 0, 1
... while b <n:
... print b,
... a, b = b, a+b
...
>;>;>; # Now call the function we just defined:
... fib(2000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597
关键字def 引入了一个函数 定义。在其后必须跟有函数名和包括形式参数的圆括号。函数体语句从下一行开始,必须是缩进的。函数体的第一行可以是一个字符串值,这 个字符串是该函数的文档字符串,也可称作docstring。
有些文档字符串工具可以在线处理或打印文档,或让用户交互的浏览代码;在你的代码 中加入文档字符串是一个好的作法,应该养成习惯。
调用函数时会为局部变量引入一个新的符号表。所有的局部变量都存储在这个局部符号表 中。引用参数时,会先从局部符号表中查找,然后是全局符号表,然后是内置命名表。因此,全局参数虽然可以被引用,但它们不能在函数中 直接赋值(除非它们用global语句命名)。
函数引用的实际参数在函数调用时引入局部符号表,因此,实参总是传值调用(这里的值总是一个 对象引用,而不是该对象的值)。4.1 一个函数被另一个函数调用时,一个新的局部符号表在调用过程中被创建。
函数定义在当前符号表中引 入函数名。作为用户定义函数,函数名有一个为解释器认可的类型值。这个值可以赋给其它命名,使其能句做为一个函数来使用。这就像一个 重命名机制:
>;>;>; fib
<function object at 10042ed0>;
>;>;>; f = fib
>;>;>; f(100)
1 1 2 3 5 8 13 21 34 55 89
你可能认为fib 不是一个函数(function),而是一个过程(procedure)。Python和C一样,过程只是一个没有返回值的函数。实际上,从技术上讲,过程也 有一个返回值,虽然是一个不讨人喜欢的。这个值被称为 None (这是一个内置命名)。如果一个值只是None的话,通常解释器不会写一个 None出来,如果你真想要看它的话,可以这样做:
>;>;>; 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 b <n:
... result.append(b) # see below
... a, b = b, a+b
... return result
...
>;>;>; f100 = fib2(100) # call it
>;>;>; f100 # write the result
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
和 以前一样,这个例子演示了一些新的Python功能:
4.7 深入函数定义
有时需要定义参数个数可变的函数。有三个方法可以做到,我们可以组 合使用它们。
4.7.1 参数默认值
最有用的形式是给一个或多个参数指定默认值。这样创建的函数可以在调用时使用更少的参数。
def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
while True:
ok = raw_input(prompt)
if ok in ('y', 'ye', 'yes'): return 1
if ok in ('n', 'no', 'nop', 'nope'): return 0
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)。
默认值在函数定义段被解析,如下所示:
i = 5
def f(arg=i):
print arg
i = 6
f()
will print 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,
print "if you put", voltage, "Volts through it."
print "-- Lovely plumage, the", type
print "-- It's", state, "!"
可以用以下的任一方法调用:
parrot(1000)
parrot (action = 'VOOOOOM', voltage = 1000000)
parrot('a thousand', state = 'pushing up the daisies')
parrot('a million', 'bereft of life', 'jump')
不过以下几种调用是无效的:
parrot() # required argument missing(缺少必要参数)
parrot(voltage=5.0, 'dead') # non-keyword argument following keyword(在关键字后面有非关键字参数)
parrot(110, voltage=220) # duplicate value for argument (对参数进行了重复赋值)
parrot(actor='John Cleese') # unknown keyword(未知关键字)
通常,参数列表中的每一个关键字都必须来自 于形式参数,每个参数都有对应的关键字。形式参数有没有默认值并不重要。实际参数不能一次赋多个值--形式参数不能在同一次调用中同 时使用位置和关键字绑定值。这里有一个例子演示了在这种约束下所出现的失败情况:
>;>;>; def function(a):
... pass
...
>;>;>; function(0, a=0)
Traceback (most recent call last):
File "<stdin>;", line 1, in ?
TypeError: function() got multiple values for keyword argument 'a'
引入一个形如 **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
keys = keywords.keys()
keys.sort()
for kw in keys: print kw, ':', keywords[kw]
它可以像这样调用:
cheeseshop('Limburger', "It's very runny, sir.",
"It's really very, VERY runny, sir.",
client='John Cleese',
shopkeeper='Michael Palin',
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 可 变参数表
最后,一个最不常用的选择是可以让函数调用可变个数的参数。这些参数被包装进一个拓扑。在这些可变个数的参数之前,可以有零 到多个普通的参数:
def fprintf(file, format, *args):
file.write(format % args)
4.7.4 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.5 文档字符串
这里介绍文档字符串 的概念和格式。
第一行应该是关于对象用途的简介。简短起见,不用明确的陈述对象名或类型,因为它们可以从别的途径了解到(除非这个名 字碰巧就是描述这个函数操作的动词)。这一行应该以大写字母开头,以句号结尾。
如果文档字符串有多行,第二行应该空出来,与接下来的 详细描述明确分隔。接下来的文档应该有一或多段描述对象的调用约定、边界效应等。
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. (本文已被浏览 次) | | |