除了前面介绍的while语句之外,Python也拥有其他语言所拥有的一般地流程控制语句,并且稍有不同之处。
1.if语句
if语句或许是最为人所知是一个流程控制语句了,有如下示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
>>> 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 |
可以有0个或多个elif分支,关键字”elif”是”else if”的缩写,这可以有效的避免过多的缩进。if…elif…elif…语句可以用来替代其他语言中的switch或case语句。
2.for语句
Python语言中的for语句有点不同于C或Pascal中的for语句,如C语言可以由用户来定义迭代步骤和终止条件。Python的for语言依据任意序列(字符串或列表)中的子项,按它们在序列中的顺序进行迭代。例如:
1 2 3 4 5 6 7 8 |
>>> # Measure some strings: ... words = ['cat', 'window', 'defenestrate'] >>> for w in words: ... print(w, len(w)) ... cat 3 window 6 defenestrate 12 |
在迭代过程中修改迭代序列不安全,如果你想要修改你迭代的序列,你可以迭代它的副本。使用切分标识就可以很方便的做到这一点:
1 2 3 4 5 6 |
>>> for w in words[:]: # Loop over a slice copy of the entire list. ... if len(w) > 6: ... words.insert(0, w) ... >>> words ['defenestrate', 'cat', 'window', 'defenestrate'] |
3.range()函数
如果你需要一个数值序列,内置函数range()会很方便,它生成一个等差链表:
1 2 3 4 5 6 7 8 |
>>> for i in range(5): ... print(i) ... 0 1 2 3 4 |
range(10)生成一个包含10个值的链表,它用链表的索引填充了这个长度为10的链表值,所生成的链表中不包括范围的结束值。也可以让range()操作从另一个数值开始,或者可以指定一个不同的步进值(也称为步长,可以为负数):
1 2 3 4 5 6 7 8 |
range(5, 10) 5 through 9 range(0, 10, 3) 0, 3, 6, 9 range(-10, -100, -30) -10, -40, -70 |
迭代链表的索引时,range()可以和len()一起使用:
1 2 3 4 5 6 7 8 9 |
>>> a = ['Mary', 'had', 'a', 'little', 'lamb'] >>> for i in range(len(a)): ... print(i, a[i]) ... 0 Mary 1 had 2 a 3 little 4 lamb |
如果你用print函数打印range,将会出现如下情况:
1 2 |
>>> print(range(10)) range(0, 10) |
其中,打印出来的并不是一个列表(在Python2.7中将打印输出列表[0,1,2,3,4,5,6,7,8,9])。在很多时候,range()函数返回的对象表现的像是一个列表,但事实上它并不是,当你迭代它时,它是一个能够像期望的序列返回连续项的对象:但为了节省空间,它并不真正构造列表。
我们称此类对象是可迭代的,即适合作为那些期望从某些东西中获得连续项知道结束的函数或结构的一个目标(参数)。我们已经见过for语句就是这样一个迭代器。list()函数就是另外一个(迭代器),它从可迭代(对象)中创建列表:
1 2 |
>>> list(range(5)) [0, 1, 2, 3, 4] |
稍后我们会看到更多返回可迭代对象和以可迭代对象作为参数的函数。
4.break和continue语句,以及循环中的else子句
和C语言类似,Python语言中的break语句也是跳出for循环或while循环中最近的一级循环体。
循环体可以有一个else子句:它在循环迭代完整个列表(对于for)或执行条件为false(对于while)时执行,但循环被break终止的情况下不会执行。如下所示搜索质数的示例程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
>>> 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语句)
与循环一起使用时,else子句与try语句的else子句比if语句的具有更多的共同点:try语句的else子句在未出现异常时运行,循环的else子句在末出现break时运行。
continue语句是从C中借鉴来的,它表示循环继续执行下一次迭代:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
>>> for num in range(2, 10): ... if num % 2 == 0: ... print("Found an even number", num) ... continue ... print("Found a number", num) Found an even number 2 Found a number 3 Found an even number 4 Found a number 5 Found an even number 6 Found a number 7 Found an even number 8 Found a number 9 |
5.pass语句
pass语句什么也不做。当程序段中需要一个语句,但又不需要该语句做任何动作的时候,就可以使用pass语句。例如:
1 2 3 |
>>> while True: ... pass # Busy-wait for keyboard interrupt (Ctrl+C) ... |
这通常用来创建最小结构的类:
1 2 3 |
>>> class MyEmptyClass: ... pass ... |
另一方面,pass可以在创建新代码时用来做函数或控制体的占位符,这样可以让你在更抽象的级别上思考。pass可以默默地被忽视:
1 2 3 |
>>> def initlog(*args): ... pass # Remember to implement this! ... |
6.定义函数
我们可以创建一个用来生成指定边界的斐波那契数列的函数:
1 2 3 4 5 6 7 8 9 10 11 |
>>> 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引入了一个函数定义。函数名跟在关键字def后面,并且函数的形参必须用括号括起来。函数体部分从下一行开始必须是缩进的。
函数体的第一行语句可以是可选的字符串文本,这个字符串是函数的文档字符串,或者称为docstrings。有些工具通过docstrings自动生成在线的或可打印的文档,或者让用户通过代码交互浏览;在你的代码中包含docstrings是一个好的实践,让它成为习惯吧。
函数调用会为函数局部变量生成一个新的符号表。确切的说,所有函数中的变量赋值都是将值存储在局部符号表。变量引用首先在局部符号表中查找,然后是在包含函数的局部符号表,接着是全局符号表,最后是内置名字表。因此,全局变量不能在函数中直接赋值(除非用global语句命名),尽管它们可以被引用。
函数引用的实际参数在在函数调用时引入局部符号表,因此,实参总是传值调用(这里的值总是一个对象引用,而不是该对象的值)。一个函数被另一个函数调用时,一个新的局部符号表在调用过程中被创建。
一个函数定义会在当前符号表内引入函数名。函数名指代的值(即函数体)有一个被Python解释器认定为用户自定义函数的类型。这个值可以赋予其他的名字(即变量名),然后它也可以被当做函数使用。这可以作为通用的重命名机制:
1 2 3 4 5 |
>>> fib <function fib at 10042ed0> >>> f = fib >>> f(100) 0 1 1 2 3 5 8 13 21 34 55 89 |
如果你使用过其他的编程语言,你可能会反驳道:fib不是一个函数而是一个procedure(is not a function but a procedure),因为它没有返回值。事实上,没有return语句的函数确实会返回一个值,虽然是一个相当令人厌烦的值(指None)。这个值被称为None(这是一个内建名称)。如果None值是唯一被书写的值,那么在写的时候通常会被解释器忽略(即不输出任何内容)。如果你确实想看到这个值的输出内容,可以使用print()函数:
1 2 3 |
>>> fib(0) >>> print(fib(0)) None |
定义一个函数返回斐波那契数列,而不是打印输出:
1 2 3 4 5 6 7 8 9 10 11 12 |
>>> 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。Falling off the end of a function also returns None.
- 语句result.append(a)是链表对象的一个方法,方法是一个“属于”某个对象的函数,它被命名为obj.methodname,这里的obj是某个对象(可能是一个表达式),methodname是某个在该对象类型中定义的方法的命名。不同的类型定义不同的方法,不同类型可能有同样名字的方法,但不会混淆。(当你定义自己的对象类型和方法时,可能会出现这种情况,class的定义方法详见类相关的内容)示例中演示的append()方法由链表对象定义,它向链表中加入一个新元素。在示例中它等同于result = result + [a],不过效率更高。
7.深入Python函数定义
你也可以定义包含若干个参数的函数,这里有三种可用的形式,也可以混合使用。
(1)默认参数值
最常用的一种形式是为一个或多个参数指定默认值。这会创建一个可以使用比定义时允许的参数更少的参数调用函数,例如:
1 2 3 4 5 6 7 8 9 10 11 |
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) |
这个函数可以以若干方式调用:
- 只给出必要的参数:
1ask_ok('Do you really want to quit?') - 给出一个可选的参数:
1ask_ok('OK to overwrite the file?', 2) - 给出所有参数:
1ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')
这个例子同样还介绍了关键字in,它测定序列中是否包含某个确定的值。
默认值在函数定义作用域被解析,例如:
1 2 3 4 5 6 7 |
i = 5 def f(arg=i): print(arg) i = 6 f() |
输出:5
重要警告:默认值只会被赋值一次。这使得当默认值是可变对象时会有所不同,比如列表、字典或者大多数的实例。例如,下面的函数在后续调用过程中会累积前面传给它的参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def f(a, L=[]): L.append(a) return L print(f(1)) print(f(2)) print(f(3)) 输出: [1] [1, 2] [1, 2, 3] |
如果你不想让默认值在后续调用中累积,可以这样定义:
1 2 3 4 5 |
def f(a, L=None): if L is None: L = [] L.append(a) return L |
(2)关键字参数
函数可以通过关键字参数的形式来调用,形如kwarg = value。例如,以下的函数:
1 2 3 4 5 |
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)。可以用以下的任一方法调用:
1 2 3 4 5 6 |
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 |
但是,下面的调用是错误的:
1 2 3 4 |
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 |
在函数调用中,关键字的参数必须跟在位置参数的后面。传递的所有关键字参数必须与函数接受的某个参数相匹配,它们的顺序并不重要。这也包括非可选参数(例如parrot(voltage=1000)也是有效的)。任何参数都不可以多次赋值。如下示例是错误的:
1 2 3 4 5 6 7 |
>>> 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之前出现)。例如,定义如下一个函数:
1 2 3 4 5 6 7 8 9 |
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]) |
对此,可以这样调用:
1 2 3 4 5 |
cheeseshop("Limburger", "It's very runny, sir.", "It's really very, VERY runny, sir.", shopkeeper="Michael Palin", client="John Cleese", sketch="Cheese Shop Sketch") |
并且,有如下输出:
1 2 3 4 5 6 7 8 |
-- 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 |
注意在打印关键字参数之前,通过对关键字字典key()方法的结果进行排序,生成了关键字参数名的列表;如果不这样做,打印出来的参数的顺序是未定义的。
(3)可变参数列表
最后,一个最不常用的选择是可以让函数调用可变个数的参数。这些参数被包装进一个元祖。在这些可变个数的参数之前,可以有零个到多个普通参数:
1 2 |
def write_multiple_items(file, separator, *args): file.write(separator.join(args)) |
通常,这些可变参数是参数列表中的最后一个,因为它们将把所有的剩余输入参数传递给函数。任何出现在*arg后的参数是关键字参数,这意味着,它们只能被用作关键字,而不是位置参数:
1 2 3 4 5 6 7 |
>>> def concat(*args, sep="/"): ... return sep.join(args) ... >>> concat("earth", "mars", "venus") 'earth/mars/venus' >>> concat("earth", "mars", "venus", sep=".") 'earth.mars.venus' |
(4)拆分参数列表
另有一种相反的情况:当你要传递的参数已经是一个列表,但要调用的函数却接受分开一个个的参数值。这时候你要把已有的列表拆开来。例如内建函数range()需要独立的start,stop参数。你可以在调用函数时加一个*操作符来自动把参数列表拆开:
1 2 3 4 5 |
>>> 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] |
以同样的方式,可以使用**操作符分拆关键字参数为字典:
1 2 3 4 5 6 7 8 |
>>> 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 ! |
(5)Lambda表达式
处于实际需要,有几种通常在函数式编程语言Lisp中出现的功能加入到Python。通过lambda关键字,可以创建短小的匿名函数。这里有一个函数返回它的两个参数的和:lambda a, b: a+b。Lambda形式可以用于任何需要的函数对象。处于语法限制,它们只能有一个单独的表达式,语义上讲,它们只是普通函数定义中的一个语法技巧。类似于嵌套函数定义,lambda形式可以从外部作用域应用变量:
1 2 3 4 5 6 7 8 |
>>> def make_incrementor(n): ... return lambda x: x + n ... >>> f = make_incrementor(42) >>> f(0) 42 >>> f(1) 43 |
上面的示例使用lambda表达式返回一个函数,另一个用途是将每一个小函数作为参数传递:
1 2 3 4 |
>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')] >>> pairs.sort(key=lambda pair: pair[1]) >>> pairs [(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')] |
(6)文档字符串
这里介绍文档字符串的概念和格式。
第一行应该是关于对象用途的简介。简短起见,不用明确的陈述对象名或类型,因为它们可以从别的途径了解到(除非这个名字碰巧就是描述这个函数操作的动词)。这一行应该以大写字符开头,以句号结尾。
如果文档字符串有多行,第二行应该空出来,与接下来的详细描述明确分隔。接下来的文档应该有一或多段描述对象的调用约定、边界效应等。
Python的解释器不会从多行的文档字符串中去除缩进,所以必要的时候应当自己清除缩进。这符合通常的习惯。第一行之后的第一个非空行决定了整个文档的缩进格式。(我们不用第一行是因为它通常紧靠着起始的引号,缩进格式显示的不清楚)留白“相当于”是字符串的起始缩进。每一行都不应该有缩进,如果有缩进的话,所有的留白都应该清除掉。留白的长度应当等于扩展制表符的宽度(通常是8个空格)。
以下是一个多行文档字符串的示例:
1 2 3 4 5 6 7 8 9 10 11 |
>>> 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. |
(7)函数注解(Function Annotations)
略
(8)编码风格(Coding Style)
略
参考:
Python 3.5.2 Documentation