Python Tutorial(四):更多的控制流工具
4.1 if语句
也许大家最熟悉的语句类型就是if语句。例如:
可以有零到多个elif部分,else部分是可选的。关键字elif是对else if的简写,在避免过多的缩进方面是有用的。一个if...elif...elif...序列是对其它语言里的switch或case语句的一种替换。
4.2 for语句
Python里面的for语句和C或Pascal里面的有点不同。它不总是在数字的算数序列上进行迭代(像Pascal),或使用户有能力定义迭代步骤和停止条件(像C),Python的for语句可以在任何序列(列表或字符串)上迭代,按照它们出现在序列中的顺序。例如:
在循环里面修改正在被迭代的序列(只可能是可修改的序列类型,像列表)不是安全的。如果你想修改正在迭代的序列(例如,重复当前的选中项),你必须在一个拷贝上迭代。切片操作可以特别方便的获得序列的拷贝:
4.3 range()函数
如果你需要在一个数字序列上迭代,内建函数range()就在手边,它产生算数序列:
给出的结束点绝不会包含在序列里,range(10)产生10个值,这些值是长度为10的序列的所有项的合法索引值。可以让range从另一个数字开始,或指定一个不同的增量(可以是负值,有时候这个增量被称为步长):
想要在一个序列的索引上进行迭代,可以结合range()和len()来使用:
在大多数这样的情况,使用enumerate()函数非常方便。
当你仅仅是打印一个range,会发生一个奇怪的事情:
在许多方面,range()返回的对象的行为就像一个列表,事实上它不是。它是一个对象,当你在它上面迭代时,它会返回期望序列的连续的项,它并不真的产生一个列表,这样可以节省空间。
我们称这样的一个对象是可迭代的,适合作为函数和构造的目标,从这些函数和构造希望获得连续的项直到这些项全部耗尽。我们已经看到for语句就是这样一个迭代器,list()函数是另一个,它从可迭代的对象创建列表:
稍后我们将看到更多返回可迭代对象的函数和使可迭代对象作为参数的函数。
4.4 break和continue语句,循环的else从句
break语句,和C里差不多,终止最小的封闭的for或while循环。
循环语句可以有一个else从句,这个从句是在循环是通过列表耗尽而终止(for语句)或循环条件变为false(while语句)时执行,当循环是通过break语句终止时并不执行。下面的循环就是它的典型,查找质数:
(这是正确的代码。esle从句属于for循环,而不是if语句。)
当用于循环时,else从句与if语句的else从句相比,它和try语句的else从句有更多的共同之处。try语句的else从句当没有异常发生时执行,循环的else从句当没有break发生时执行。
continue语句,也是从C里面照搬过来的,停止本次循环,继续下次循环:
4.5 pass语句
pass语句什么都不做,它适于用当从语法上讲要求有一个语句,但是从程序来讲不需要执行动作。例如:
它通常用于创建最小化的类:
另一个pass可以使用的地方是作为函数的占位符或者当你写新代码时的条件体,允许你在更抽象的级别思考。pass语句会被安静的忽略:
4.6 定义函数
我们可以创建一个函数来输出任意边界的斐波那契序列:
关键字def表明这是一个函数定义。它后面必须跟一个函数名称和括号括起来的形式参数列表。从下一行开始,语句构成了函数体,它们必须缩进。
函数体的第一条语句可选为一个字符串字面量,这个字符串字面量是函数的文档字符串。有一些工具使用这些文档字符串自动产生在线的或打印的文档,或让用户交互的通过代码浏览,当你写代码的时候加上文档字符串是一个很好的实践,并使它成为一种习惯。
函数的执行引进一个新的符号表被函数的本地变量使用。更加准确的说,一个函数里所有的变量赋值都把值存储到了本地符号表,然后变量引用首先搜索本地变量表,然后封闭函数的本地变量表,然后全局符号表,最后内建名称表。因此,全局变量不能在一个函数里面直接被赋值(除非被命名在一个global语句中),虽然它们可以被引用到。
当一个函数被调用时,这个函数调用的实际参数被引入到被调函数的本地符号表,因此,参数是通过传值传递的(这个值总是对象引用,不是对象的值)。当一个函数调用另一个函数时,一个新的本地符号表被创建为了那个调用。
函数定义把函数名称引入到当前符号表。函数名称的值有一个类型,这个类型被解释器作为用户定义函数来认知。这个值可以被赋给另外一个名称,它也被作为函数使用。它是由通用命名机制实现的:
对于其它语言来说,fib对象不是一个函数,而是一个过程,因为它不返回值。事实上,即使函数没有一个return语句也会返回一个值,即使是一个无聊的值。这个值叫做None(这个一个内建的名字)。写入值None通常会被解释器阻止,如果它是唯一被写的值。如果你真的想看它的话就使用print():
写一个函数把斐波那契序列作为列表返回非常简单,而不是打印它:
这个示例演示了一些Python新特性:
- return语句从函数返回一个值。return后面如果没有跟表达式就返回None。如果没有到函数结束的话也返回None。
- 语句result.append(a)调用了列表对象result的一个方法。一个方法就是一个函数,这个函数属于一个对象,被命名为obj.methodname,这里的obj是某个对象(可以是一个表达式),methodname是被这个对象类型定义的一个方法的名字。不同的类型定义不同的方法。不同类型的方法可以拥有同样的名字,不会因此歧义。示例里面的append()方法是为列表对象定义的,它在列表的末尾添加一个新元素。在这个示例里面它等同于result = result + [a],但是更有效。
4.7 更多有关定义函数
也可以定义有可变数目参数的函数。参数有三种形式,它们可以结合在一起。
4.7.1 参数的默认值
最有用的形式是为一个或多个参数指定默认值。这样创建的函数在调用时可以传入比定义时更少的参数。例如:
这个函数可以以几种方式调用:
- 只给出必须的参数: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关键字,来测试一个序列是否包含一个确定的值。
这些默认值在函数定义的时候被计算,是在定义的范围中。所以:
将打印5。
重要警告:默认值只被计算一次。当默认值是一个可变对象时会有一个不同之处,如列表,字典或许多类的实例。下面的函数在后续的调用中会累积传给它的参数:
这将打印:
如果你不想这些默认的参数在后续的调用中共享,你可以像这样来实现函数:
4.7.2 关键字参数
函数也可以使用kwarg=value这种形式的关键字参数进行调用。例如,下面这个函数:
接收一个必填的参数(vltage)和三个可选的参数(state,action,和type)。可以使用下面的任何一种方式调用:
但是下面的调用都是非法的:
在一个函数调用中,关键字参数必须跟在位置参数后面。所有被传进去的关键字参数必须匹配这个函数接收参数中的一个(actor对函数parrot是一个非法的参数),它们的顺序并不重要。这同样包括非可选参数(parrot(voltage=1000)是合法的)。没有参数可以多次接收一个值。这个示例是失败的由于这个约束:
当最后一个形式参数是以**name这种形式出现的,它将接收一个字典包含所有的除了那些形式参数以外的所有关键字参数。它可以和*name这种形式的形式参数结合起来,它接收一个元祖包含超出形式参数以外的所有位置参数。*name必须在**name前面。例如,如果我们定义一个函数像这样:
它可以像这样被调用:
当然,它将打印:
注意,关键字参数名称的列表是在打印出它们的内容前通过对关键字字典的keys()方法的结果进行排序而创建的;如果不这样做的话,列表里面的参数被打印的顺序是没有定义的。
4.7.3 任意参数列表
最后,使用频率最少的是一个函数可以使用任意数目的参数进行调用。这些参数将被包装在一个元祖里面。在这些可变数目参数之前,可以有零或多个形式参数:
通常,这些可变参数都在形参列表的最后面,因为它们收集所有传入函数的剩余的输入参数。出现在*args后面的任何形式参数只能是关键字参数,意味着它们只能用作关键字参数而不是位置参数:
4.7.4 解压参数列表
当参数已经在一个列表里或元祖里,但是函数调用需要解压它们成为单独的位置参数,这是相反的情况就发生了。例如,内建range()函数希望单独的开始和结束参数。如果它们不能单独的得到,函数调用时使用*操作符来把参数从列表或元祖中解压出来:
以同样的方式,字典可以使用**操作符来分发关键字参数:
4.7.5 lambda形式
通过大众需求,很少的一些通常在函数式编程语言(像Lisp)里被发现的新特性已经被添加到Python里。使用lambda关键字,很小的匿名函数可以被创建。这是一个返回它的两个参数之和的函数:lambda a, b: a+b。lambda形式可以应用在任何要求函数对象的地方。它们在语法上限制为一个单一的表达式。在语义上,它们仅仅是正常函数定义的语法糖。像嵌套函数定义,lambda形式可以引用来自于包含它的范围里面的变量:
本文是对官方网站内容的翻译,原文地址:http://docs.python.org/3/tutorial/controlflow.html