Python Tutorial第四章 更多的控制工具

来源:互联网 发布:阿里云幕布免费不 编辑:程序博客网 时间:2024/05/22 06:15
 

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'
...

可以有零个或多个elif部分.else部分是可选的.关键字elifelse if的缩写,可以消除过度的缩进.if…elif…elif…序列是其它语言中switchcase语句的一个替代.


4.2
for 语句

Python中的for语句和你在CPascal中见到的有一点不同.Pascal里只在数字中迭代或者C中允许用户自定义迭代过程和终止条件不同,Pythonfor语句在序列(list或者string,以及更多)的元素上迭代.迭代以元素在序列中的顺序进行.例如:

>>> # Measure some strings:
... a = ['cat', 'window', 'defenestrate']
>>> for x in a:
...     print x, len(x)
... 
cat 3
window 6
defenestrate 12

改变正在迭代的序列是不安全的(这种情况只会发生在可变序列,如列表上).如果需要修改正在迭代的列表(例如,复制选中的元素),你必须在序列的一个拷贝上迭代.切片表示使这一切变得很方便:

>>> for x in a[:]: # make a slice copy of the entire list
...    if len(x) > 6: a.insert(0, x)
... 
>>> a
['defenestrate', 'cat', 'window', 'defenestrate']


4.3
range() 函数

如果需要在数字序列上迭代,内置函数range()是很好的选择.它产生包含算术数列的列表:

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

给出的终点不是列表的一部分. range(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[i]
... 
0 Mary
1 had
2 a
3 little
4 lamb


4.4
break continue 语句,以及循环中的else语句

break语句和C中类似,从内层的for或者while循环中跳出.

continue 语句同样来源于C, 开始循环中的下一个迭代.

循环语句也可以拥有一个else部分;当列表结束(for语句)或者条件为假(while语句)时就会执行else部分.但循环以break终止时不会执行else部分.下面这个寻找素数的例子可以说明这一点:

>>> 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语句不作任何事情.当语法上需要一个语句,但程序不需要任何操作时使用pass语句.例如:

>>> while True:
...       pass # Busy-wait for keyboard interrupt
...


4.6
定义函数

我们可以创建一个输出任何范围以内Fibonacci数列的函数:

>>> 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 引入一个函数定义.后面必须跟着函数名和由小括号括起的命名参数(formal parameter).函数体从下一行开始,必须有缩进.函数第一行也可以是一个字符串.这个字符串是函数的文档字符串(document string),docstring. 一些工具使用docstring自动产生在线或打印文档,或者让用户交互浏览代码.在代码中使用docstring是一个好的实践,就让它成为你的一个习惯吧.

函数的执行引入一个新的包含局部变量的符号表.更准确的说,所有函数内的变量赋值都把值保存在局部符号表中。对变量的引用首先查找局部符号表,然后是全局符号表,其次是内置名称表。因此全局变量尽管可以被引用,但不能在函数中直接赋值(除非用global关键字声明)

实参放在被调用函数的局部符号表中。因此参数是按值传递的(这个值总是对对象的引用,不是对象的值[1])。当一个函数调用另一个函数时,Python会为被调用函数创建一个新的符号表。

函数定义在当前符号表中引入了函数名。函数名对应的值具有"用户定义的函数"类型。Python解释能识别这种类型。该值可以被赋给另一个名称。赋值后另一个名称也可以当函数使用。这是一种通用的重命名机制。

>>> fib
<function fib at 10042ed0>
>>> f = fib
>>> f(100)
1 1 2 3 5 8 13 21 34 55 89

You might object that fib is not a function but a procedure. In Python, like in C, procedures are just functions that don't return a value. In fact, technically speaking, procedures do return a value, albeit a rather boring one. This value is called None (it's a built-in name). Writing the value None is normally suppressed by the interpreter if it would be the only value written. You can see it if you really want to:

你可能会质疑说fib不是一个函数,而是一个过程。在Python中和C中一样,过程是不返回值的函数,从技术上讲,过程实际上返回了一个值,一个没有意义的值。这个值叫做None(一个内置名称)。如果只有一个None值需要输出,解释器不会输出。如果你真的想看到返回的None值也是可以的:

>>> print fib(0)
None

It is simple to write a function that returns a list of the numbers of the Fibonacci series, instead of printing it:

写一个返回包含部分Fibonacci数列的函数,而不是打印它们是很简单的:
>>> 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的一些新特性:

· return语句从函数中返回一个值。不带表达式参数的return语句返回None。函数结尾也自动返回None.

·             result.append(b)语句调用了list对象result的一个方法。方法是属于某个对象的函数,记作obj.methodname. obj是某个对象,也可以是一个表达式。Methodname是该对象的类型定义的一个名称。不同的类型拥有不同的方法。不同类型的方法可以同名而不会引起混淆.(可以使用类来定义你自己的对象类型和相关方法,见本教程稍后部分).例子中的append()方法是list类型的对象定义的。它在list的末端添加一个新的元素。本例中它相当于"result=result+[b]",但更高效.


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 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).

这个例子还引入了关键字in.in用来测试一个给定的值是否在某个序列中。

缺省值在函数定义时被计算,因此:

i = 5
 
def f(arg=i):
    print arg
 
i = 6
f()

会输出5

下面这个例子是译者加的,大家随便看看也就是了。

>>> l = [1,2,3]

>>> def f(fl = l): #这里fl被初始化为l指向的对象。

       print fl

 

      

>>> f

<function f at 0x00DB1770>

>>> f()

[1, 2, 3]

>>> l.append(4) #这时lfl指向同一个对象,改变l指向的对象同时就改变了fl指向的对象

>>> f()

[1, 2, 3, 4]

>>> l = [2,3]

>>> f()

[1, 2, 3, 4]

>>> 

重要提示:缺省值只计算一次。这使得缺省值是一个可变对象如列表,字典或者大多数类的实例时会有些许的不同。例如,下面的函数会在一系列调用中累加传给它的参数:

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包含了所有不在正式列表里的按位置表示的参数(*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:

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()方法在打印字典内容之前被调用。如果没有调用sort(),打印参数的顺序是未定义的。

4.7.3 任意的参数列表

最后提到的是最不常用的一种函数调用方式:以任意数目的参数调用函数。这些参数会被封装到一个元组中。在这些可变数目的参数前会有0到多个普通参数。

def fprintf(file, format, *args):
    file.write(format % args)

4.7.4 解包参数列表

相反的情形是参数已经在列表或元组中,但需要解包成独立的位置参数以满足函数调用的要求。例如内置的range()函数需要独立的startstop参数。如果不能单独得到这些参数,使用*操作符从列表或者元组中解包参数:

>>> range(3, 6)             # normal call with separate arguments
[3, 4, 5]
>>> args = [3, 6]
>>> range(*args)            # call with arguments unpacked from a list
[3, 4, 5]

同理,字典也可以用**操作符解包为键值形式:

**-operator:

>>> def parrot(voltage, state='a stiff', action='voom'):
...     print "-- This parrot wouldn't", action,
...     print "if you put", voltage, "volts through it.",
...     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表达式可以用在任何需要函数对象的场合。它们在语法上被限制只能使用一个表达式。语义上它们只是普通函数定义的补充。(Semantically, they are just syntactic sugar for a normal function definition.)和嵌套函数定义一样,lambda表达式可以引用包含它的作用域的变量。

>>> def make_incrementor(n):
...     return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43

4.7.6 文档字符串

这里是一些新的关于文档字符串内容和格式的惯例。

第一行是关于对象的短而精确的概括。为简洁明了,他不必详细陈述对象的名称或类型,因为有其它方法完成这些任务(除了这个名字正好是一个描述函数操作的动词)。这一行应该以大写字母开头而以句号结束。

如果文档字符串不止一行,第二行应当为空。这样能从视觉上把概括性的语句和描述性的部分分开。后面的部分应当包括一个或多个段落以描述函数调用约定,副作用等。

Python解释器并不会从多行字符串中删除缩进。因此处理文档的工具必要时需要删除缩进。可以用下面的方式完成:文档字符串中第二个非空行决定了整个文档字符串的缩进量(不能使用第一行因为它经常跟在字符串开始的引号后以致它的缩进并不能表示字符串中其他部分的缩进)。相当于这些数目的缩进被从后面的行中删除。不应该有小于这些缩进的行。但如果真的有的话,该行所有的前导空格都会被删除。等量的空格会在tab扩展(通常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.

 



[1] 实际上,对象引用调用是一个更好的表述。因为传递一个可变的对象时,对对象所做的任何修改对调用者都是可见的(如在列表中插入元素)