您现在的位置: IT技术文档中心 >> 文档资源 >> 编程语言及开发环境 >> Python >> 文档正文
Python入门(一)
作者:未知 文章来源:互联网 点击数: 更新时间:2007-7-20 22:48:40

第四章 流程控制
前面我们已经见到了如何由用while结构控制流程运行。这一章我们介绍更多的控制结构。Python具有和其它语言类似的控制结构但略有差别。
4.1 If 语句
If 语句可能是最基本的程序分支语句了。例如:
>>> 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部分可选。关键字elif是else if的缩写,这样可以缩短语句行长度。其它语言中switch 或 case 语句可以用if...elif...elif...语句组来实现。
4.2 for 语句
Python中的for语句与你可能熟悉的C或者Pascal中的相应语句略有不同。不象Pascal 那样总是对数字序列进行循环,也不是象C中那样完全由程序员自由地控制循环条件和循环体,Python的for循环是对任意种类的序列(如列表或字符串)按出现次序遍历每一项。例如:
>>> # 计算字符串长:
... a = ['cat', 'window', 'defenestrate']
>>> for x in a:
...     print x, len(x)
... 
cat 3
window 6
defenestrate 12
>>>

尽量不要在循环体内修改用来控制循环的序列(当然,只有可变的序列类型如列表才有可能被修改),这样程序可能会出问题。如果需要这样,比如说要复制某些项,可以用序列的副本来控制循环。片段记号让你很容易生成副本:
>>> for x in a[:]: # 生成整个列表的片段副本
...    if len(x) > 6: a.insert(0, x)
... 
>>> a
['defenestrate', 'cat', 'window', 'defenestrate']
>>>

结果是把列表中长度超过6个字符的字符串插入到列表开头。 
4.3 range() 函数
如果确实需要对一列数字进行循环的话,可以使用内置函数range()。它生成包含数字序列的列表,如:
>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>

注意给出的终点永远不出现在生成的列表中,range(10)生成一个十个数的列表,恰好是长度为10的序列的合法下标的各个值。也可以指定不同的起始点,或者指定不同的间隔(甚至负数):
>>> 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子句
如同C语言一样,break语句跳出其所处的最内层的for 或 while循环,continue语句继续下一循环步。
循环语句还可以带一个 else 子句,当循环正常结束时执行其内容,但如果循环是用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:
...          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 1:
...       pass # 等待键盘中断
...
 

4.6 函数定义
我们可以定义一个函数用来计算某一界限以下的所有Fibonacci序列值: 
>>> def fib(n):    # 写出 n 以下的所有Fibonacci序列值
...     a, b = 0, 1
...     while b < n:
...           print b,
...           a, b = b, a+b
... 
>>> # 调用刚刚定义的函数:
... fib(2000)
 
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597
>>>

其中关键字 def 开始一个函数定义,其后应该是函数名,括号内的形参表,以冒号结束。构成函数体的各语句从下一行开始,用一个制表符缩进。函数的第一个语句可以是一个字符串,如果是的话,这个字符串就是函数的文档字符串,简称为docstring。有一些工具可以利用文档字符串自动生成可打印的文档,或者让用户交互地浏览代码,所以在自己编程时加入文档字符串是一个好习惯,应该养成这样的习惯。
函数在执行时对局部变量引入一个新的符号表。函数中的变量赋值都存入局部符号表;引用变量时变量名先从局部符号表中查找,然后在全局符号表中查找,最后从内置的名字中查找。因此,在函数中不能直接对全局变量赋值(除非用了global语句来说明),但可以引用全局变量的值。
函数调用的实参被引入函数的局部符号表,即函数的参数是按值调用的。函数再调用其它函数时为该函数生成一个新的符号表。但是严格地说,函数的调用是按引用调用的,因为如果参数是一个可变类型如列表的话在函数中改变形参的内容将导致实参的内容被改变(不改变的是实参名字的绑定关系)。
函数定义把函数名放入当前符号表。函数名的值类型为用户自定义函数,这个值可以赋给另一个名字,从而这个名字也代表相同的函数。这可以作为一般的改名方法:
 
>>> fib
<function object at 10042ed0>
>>> f = fib
>>> f(100)
1 1 2 3 5 8 13 21 34 55 89
>>>
 

你可能会说 fib 不是函数而是过程。Python和C一样,过程只是不返回值的函数。实际上,严格地说,过程也返回一个值,只不过是一个很没意思的值。这个值叫做 None(这是一个内置的名字)。解释程序交互运行时如果只需要显示这个值的话就会忽略不显示。如果希望显示的话可以用 print 语句:
 
>>> print fib(0)
None
>>>
 
也可以写一个函数返回Fibonacci 序列的数值列表而不是显示这些值:
 
>>> def fib2(n): # 返回直到n的Fibonacci 序列值
...     result = []
...     a, b = 0, 1
...     while b < n:
...           result.append(b)    # 解释见下面
...           a, b = b, a+b
...     return result
... 
>>> f100 = fib2(100)    # 调用
>>> f100                # 输出结果
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
 
>>>
 

这个例子也演示了新的Python特色:return语句从函数中退出并返回一个值。不带返回值的return可以从过程中间退出,运行到过程的末尾也可以退出,这两种情况下返回None。
语句result.append(b)调用列表对象result的一个方法。方法是“属于”一个对象的函数,引用格式为obj.methodname,其中obj是某个对象(也允许是一个表达式), methodname 是由该对象的类型定义的一个方法的名字。不同的不同的方法。不同类型的方法可以使用相同的名字而不致引起误解。(可以定义自己的对象类型和方法,使用类,本文后面会讨论这个话题)。例子中的append()方法时列表对象的方法,它在列表末尾增加一个新元素。在本例中这等价于“result = result + [b]”,只是更有效。
4.7 函数参数
 可以定义使用可变个数参数的函数。这样的定义方法有三种,可以联合使用。 
4.7.1 参数缺省值
可以为一个参数或几个参数指定缺省值。这样定义的函数在调用时实参个数可以比定义时少。例如:
def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
    while 1:
        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()

将显示5。 
注意:缺省值只计算一次。当缺省值是可变对象如列表或字典时这一点是要注意的。例如,以下函数会在以后的调用中累加它的值:
def f(a, l = []):
    l.append(a)
    return l
print f(1)
print f(2)
print f(3)
This will print 
 
[1]
[1, 2]
[1, 2, 3]

如果你不希望缺省值在连续的调用中被保留,可以象下面这样改写函数:
def f(a, l = None):
    if l is None:
        l = []
    l.append(a)
    return l

4.7.2 关键字参数
函数调用时也可以象“关键字 = 值”这样指定实参,其中关键字是定义时使用的形参的名字。例如:
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()                     # 非缺省的参数没有提供
parrot(voltage=5.0, 'dead')  # 关键字参数后面又出现了非关键字参数
parrot(110, voltage=220)     # 参数值重复提供
parrot(actor='John Cleese')  # 未知关键字

一般说来,实参表中位置参数在前,关键字参数在后,关键字名字必须是形参名字。形参有没有缺省值都可以用关键字参数的形式调用。每一形参至多只能对应一个实参,因此,已经由位置参数传入值的形参就不能在同一调用中再作为关键字参数。
如果形参表中有一个形为**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.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

4.7.3 任意个数参数
在所有有名的形参的后面可以有两个特殊的形参,一个以*args的形式命名,一个以**kw 的形式命名。有了*args形式的形参后函数在调用时就可以在正常的能匹配的实参表后面输入任意个数的参数,这些参数组成一个序表赋给args形参,不能匹配的关键字参数组成一个字典赋给kw形参。在任意个数形参之前可以有0到多个正常的参数。例如:
def fprintf(file, format, *args):
    file.write(format % args)

4.7.4 Lambda形式
因为许多人的要求,Python中加入了一些在函数编程语言和Lisp中常见的功能。可以用lambda 关键字来定义小的无名函数。这是一个返回其两个参数的和的函数:“lambda a, b: a+b” 。Lambda形式可以用于任何需要函数对象的地方。从句法上讲lambda形式局限于一个表达式。从语义上讲,这只是正常的函数定义的句法甜食。像嵌套函数定义一样,lambda形式不能访问包含其定义的作用域中的变量,但审慎地使用缺省参数之可以绕过这个限制。例如:
def make_incrementor(n):
    return lambda x, incr=n: x+incr

4.7.5 文档字符串
关于文档字符串的内容与格式正在形成一些惯例。第一行应该为简短的对象目的概括说明。为了简明起见,这一行不应该提及对象的名字或类型,因为这些可以通过其他途径得知(当然如果对象名字就是一个描述函数操作的动词则当然可以提及其名字)。着以行应该用大些字母开始,以句点结尾。如果文档字符串中有多行,第二行应该是空行,把概括说明与其它说明分开。以下的行可以是一段或几段,描述对象的调用方法,它的副作用,等等。
Python的扫描程序不会从多行字符串中去掉缩进空白,所以处理文档的工具需要自己处理缩进。只要遵循如下的惯例就可以有利于缩进空白的处理。在第一行之后的第一个非空白的行决定整个文档字符串的缩进数量(我们不用第一行,因为它经常是直接跟在表示字符串开始的引号后面)。文档字符串中除第一行以外的各行都要删除等价于此行的缩进量的空白。对制表符将扩展为空格后再删除。

第五章 Python数据结构
本章更详细地讨论一些已经讲过的数据类型的使用,并引入一些新的类型。 
5.1 列表
列表数据类型还有其它一些方法。下面是列表对象的所有方法: 
insert(i, x) ---- 在指定位置插入一项。第一自变量是要在哪一个元素前面插入,用下标表示。例如,a.insert(0, x)在列表前面插入,a.insert(len(a), x)等价于a.append(x) 。 
append(x) ---- 等价于a.insert(len(a), x) 
index(x) ---- 在列表中查找值x然后返回第一个值为x的元素的下标。没有找到时出错。 
remove(x) ---- 从列表中删去第一个值为x的元素,找不到时出错。 
sort() ---- 对列表元素在原位排序。注意这个方法改变列表,而不是返回排序后的列表。 
reverse() ---- 把列表元素反序。改变列表。 
count(x) ---- 返回x在列表中出现的次数。 
 下例使用了所有的列表方法: 
>>> a = [66.6, 333, 333, 1, 1234.5]
>>> print a.count(333), a.count(66.6), a.count('x')
2 1 0
>>> a.insert(2, -1)
>>> a.append(333)
>>> a
[66.6, 333, -1, 333, 1, 1234.5, 333]
>>> a.index(333)
1
>>> a.remove(333)
>>> a
[66.6, -1, 333, 1, 1234.5, 333]
>>> a.reverse()
>>> a
[333, 1234.5, 1, 333, -1, 66.6]
>>> a.sort()
>>> a
[-1, 1, 66.6, 333, 333, 1234.5]

5.1.1 函数程序设计工具
Python中有一些函数程序设计风格的东西,例如前面我们看到的lambda形式。关于列表有三个非常有用的内置函数:filter(), map()和reduce()。
“filter(函数, 序列)”返回一个序列(尽可能与原来同类型),序列元素是原序列中由指定的函数筛选出来的那些,筛选规则是“函数(序列元素)=true”。filter()可以用来取出满足条件的子集。例如,为了计算一些素数:
>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> filter(f, range(2, 25))
[5, 7, 11, 13, 17, 19, 23]

“map(函数,序列)”对指定序列的每一项调用指定的函数,结果为返回值组成的列表。map() 可以对序列进行隐式循环。例如,要计算三次方,可用:
>>> def cube(x): return x*x*x
...
>>> map(cube, range(1, 11))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]

可以有多个序列作为自变量,这时指定的函数必须也有相同个数的自变量,函数从每个序列分别取出对应元素作为自变量进行调用(如果某个序列比其它的短则取出的值是None)。如果指定的函数是None,map()把它当成一个返回自己的自变量的恒同函数。在函数用None的情况下指定多个序列可以把多个序列搭配起来,比如“map(None, list1, list2)”可以把两个列表组合为一个成对值的列表。见下例:
>>> seq = range(8)
>>> def square(x): return x*x
...
>>> map(None, seq, map(square, seq))
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25), (6, 36), (7, 49)]

“reduce(函数, 序列)”用来进行类似累加这样的操作,这里的函数是一个两个子变量的函数,reduce()先对序列的前两项调用函数得到一个结果,然后对结果和序列下一项调用函数得到一个新结果,如此进行到序列尾部。例如,要计算1到10的和:
>>> def add(x,y): return x+y
...
>>> reduce(add, range(1, 11))
55

如果序列中只有一个值则返回这个值,序列为空时会产生例外。可以指定第三个自变量作为初始值。有初始值时对空序列函数将返回初始值,否则函数先对初始值和序列第一项作用,然后对结果和序列下一项作用,如此进行到序列尾。例如:
>>> def sum(seq):
...     def add(x,y): return x+y
...     return reduce(add, seq, 0)
... 
>>> sum(range(1, 11))
55
>>> sum([])
0

5.2 del语句
上面我们看到,列表的remove()方法可以从列表中删去某个取值的项,我们还可以用del 语句来删除指定下标的项。也可以用del语句从列表中删除一个片断(前面我们是用给片断赋空列表的办法删除片断的)。例如:
>>> a
[-1, 1, 66.6, 333, 333, 1234.5]
>>> del a[0]
>>> a
[1, 66.6, 333, 333, 1234.5]
>>> del a[2:4]
>>> a
[1, 66.6, 1234.5]

del也可以用来删除整个变量,例如:
>>> del a
变量删除以后再引用该变量就会出错(除非又给它赋值了)。后面我们还会看到del的其它一些应用。
5.3 序表和序列
我们看到列表和字符串有许多共同点,例如,下标和片断运算。它们都属于序列数据类型。因为Python是一个正在不断发展的语言,以后还可能会加入其它的序列数据类型。现在还有一种标准的序列数据类型,称为序表(tuple)。
序表由一系列值用逗号分隔而成,例如: 

>>> t = 12345, 54321, 'hello!'
>>> t[0]
12345
>>> t
(12345, 54321, 'hello!')
>>> # 序表允许嵌套:
... u = t, (1, 2, 3, 4, 5)
>>> u
((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))

输出的序表总是用括号包围,这样可以保证嵌套序表得以正确解释。输入时可以有括号也可以没有括号,当经常是必须有括号(如果序表是一个大表达式的一部分)。
序表有许多用处,例如,(x,y)坐标对,数据库中的职工纪录,等等。序表与字符串一样是不可变的:不允许对序表的某一项赋值。
生成序表时对0项或1项的序表有特殊的规定:空序表用一对空括号表示;只有一项的序表用一个之后面跟一个抖好表示(指把这个值放在括号内是不够的)。这样写不够美观,但很有效。例如:
>>> empty = ()
>>> singleton = 'hello',    # <-- note trailing comma
>>> len(empty)
0
>>> len(singleton)
1
>>> singleton
('hello',)

语句t = 12345, 54321, 'hello!'是序表打包的一个实例:12345, 54321和'hello!'这些值被打包进了一个序表中。相反的操作也是允许的,例如:
>>> x, y, z = t
这叫做序表解包。序表解包要求等号左边的变量个数等于序表的长度。注意多重赋值只是序表打包和序表解包的联合使用。有时也对列表进行类似操作,即列表解包。只要把各变量写成一个列表就可以进行解包:
>>> a = ['spam', 'eggs', 100, 1234]
>>> [a1, a2, a3, a4] = a

5.4 字典
Python内置的另一个有用的数据类型是字典。字典在其它语言中有时被称为“关联记忆” 或“关联数组”。字典不象序列,它不是用在一个范围之内的数字下标来索引,而是用键值来索引,键值可以是任何不可变类型。字符串和数值总可以作键值。如果序表只包含字符串、数值或序表则序表也可以作键值使用。列表不能用作键值,因为列表可以用其append()方法就地改变值。
最好把字典看成是一系列未排序的“键值:值”的集合,在同一字典内键值是互不相同的。一对空大括号产生一个空字典:{}。在大括号内加入用逗号分开的“键值:值”对可以在字典内加入初始的键值和值对,字典在输出时也是这样显示的。对字典的主要操作是以某个键值保存一个值,以及给定键值后查找对应的值。也可以用del删除某个键值:值对。如果用一个已有定义的键值保存某个值则原来的植被遗忘。用不存在的键值去查找会出错。
字典对象的keys()方法返回字典中所有键值组成的列表,次序是随机的。需要排序时只要对返回的键值列表使用sort()方法。为了检查某个键值是否在字典中,使用字典的has_key() 方法。
下面是字典使用的一个简单例子: 
>>> tel = {'jack': 4098, 'sape': 4139}
>>> tel['guido'] = 4127
>>> tel
{'sape': 4139, 'guido': 4127, 'jack': 4098}
>>> tel['jack']
4098
>>> del tel['sape']
>>> tel['irv'] = 4127
>>> tel
{'guido': 4127, 'irv': 4127, 'jack': 4098}
>>> tel.keys()
['guido', 'irv', 'jack']
>>> tel.has_key('guido')
1

5.5 条件的进一步讨论
在while语句和if语句中使用的条件除了可以使用比较之外还可以包含其它的运算符。比较运算符“in”和“not in”可以检查一个值是否在一个序列中。运算符“is”和“is not ”比较两个对象是否恰好是同一个对象,这只对象列表这样的可变对象有意义。所有比较运算优先级相同,而比较运算的优先级比所有数值运算优先级低。
比较允许连写,例如,a < b == c检查是否a小于等于b而且b等于c。 
比较可以用逻辑运算符and和or连接起来,比较的结果(或其它任何逻辑表达式)可以用not 取反。逻辑运算符又比所有比较运算符低,在逻辑运算符中,not优先级最高,or的优先级最低,所以“A and not B or C”应解释为“(A and (not B)) or C”。当然,可以用括号来表示所需的组合条件。
逻辑运算符and和or称为“短路”运算符:运算符两侧的表达式是先计算左边的,如果左边的结果已知则整体结果已知就不再计算右边的表达式。例如,如果A和C为真而B为假则“A and B and C”不会计算表达式C。一般地,当短路运算符的运算结果不是用作逻辑值的时候返回的是最后求值的那个表达式的值。
可以把比较或其它逻辑表达式的结果赋给一个变量。例如: 
>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
>>> non_null = string1 or string2 or string3
>>> non_null
'Trondheim'

注意Python和C不同,表达式中不能进行赋值。 
5.6 序列与其它类型的比较
序列对象可以和其它同序列类型的对象比较。比较使用字典序:先比较最前面两项,如果这两项不同则结果可以确定;如果这两项相同,就比较下面的两项,如此下去,直到有一个序列到头为止。如果某两项本身也是同类型的序列,则进行递归的字典序比较。如果两个序列的所有各项都相等,则这两个序列相等。如果一个序列是另一个序列的一个初始子序列,短的一个是较小的一个。字符串的字典序比较按各个字符的ASCII次序进行。下面是一些序列比较的实例:
(1, 2, 3)              < (1, 2, 4)
[1, 2, 3]              < [1, 2, 4]
'ABC' < 'C' < 'Pascal' < 'Python'
(1, 2, 3, 4)           < (1, 2, 4)
(1, 2)                 < (1, 2, -1)
(1, 2, 3)              = (1.0, 2.0, 3.0)
(1, 2, ('aa', 'ab'))   < (1, 2, ('abc', 'a'), 4)

注意不同类型的对象比较目前也是合法的。结果是确定的但却没有什么意义:不同类型是按类型的名字排序的。所以,列表(list)总是小于字符串(string),字符串总是小于序表(tuple),等等。但是程序中不能依赖这样的比较规则,语言实现可能会改变。不同的数值类型可以按数值来比较,所以0等于0.0,等等。

第六章 模块
如果退出Python解释程序然后再进入,原有的定义(函数和变量)就丢失了。所以,如果需要写长一点的程序,最好用一个文本编辑程序为解释程序准备输入,然后以程序文件作为输入来运行Python解释程序,这称为准备脚本(script)。当你的程序变长时,最好把它拆分成几个文件以利于维护。你还可能想在几个程序中都使用某个很方便的函数,但又不想把函数定义赋值到每一个程序中。
为了支持这些,Python有一种办法可以把定义放在一个文件中然后就可以在一个脚本中或交互运行中调用。这样的文件叫做一个模块;模块中的定义可以导入其它模块或主模块(主模块指在解释程序顶级执行的脚本或交互执行的程序所能访问的变量集合)。
模块是包含了Python定义和语句的文件。文件名由模块名加上后缀“.py”构成。在模块内,模块的名字(作为一个字符串)可以由全局变量__name__的值获知。例如,在Python的搜索路径中用你习惯使用的文本编辑器(Python 1.5.2包含了一个用Tkinter编写的IDLE集成开发环境,MS Windows下有一个PythonWin界面也可以进行Python程序编辑)生成一个名为“fibo.py ”的文件,包含如下内容:
# Fibonacci numbers module
 
def fib(n):    # 输出小于n的Fibonacci序列
    a, b = 0, 1
    while b < n:
        print b,
        a, b = b, a+b
 
def fib2(n): # 返回小于n的Fibonacci序列
    result = []
    a, b = 0, 1
    while b < n:
        result.append(b)
        a, b = b, a+b
    return result

然后进入Python解释程序(在IDLE或PythonWin中可以直接进入解释程序窗口),用如下命令可以导入模块:
>>> import fibo
这不会把模块fibo中的函数的名字直接引入当前的符号表,这只是把模块名fibo引入。可以用模块名来访问其中的函数:
>>> fibo.fib(1000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
>>> fibo.fib2(100)
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
>>> fibo.__name__
'fibo'

如果经常使用某个函数可以给它赋一个局部名字:
>>> fib = fibo.fib
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377

6.1 模块的进一步介绍
模块除了可以包含函数定义之外也可以包含可执行语句。这些可执行语句用来初始化模块,它们只在模块第一次被导入时执行。
每个模块有自己私有的符号表,这个私有符号表对于模块中的所有函数而言却是它们的全局符号表。因此,模块作者可以在模块中使用全局变量而不需担心与模块用户的全局变量冲突。另一方面,如果你有把握的话也可以用访问模块中函数的格式,即modname.itemname的方法来修改模块中的全局变量。
模块可以导入其它模块。我们通常把所有的导入语句放在模块(或脚本)的开始位置,这不是规定要求的。导入的模块名放入模块的全局符号表中。
导入还有另一种用法,可以把模块中的名字直接导入使用者的符号表。例如: 
>>> from fibo import fib, fib2
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377

这不会把模块名导入使用者的符号表中(例如,上面例子中fibo就没有定义)。 
还有一种办法可以导入一个模块中定义的所有名字: 
>>> from fibo import *
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377

这可以把模块中除了以下划线结尾的所有名字导入。 
6.1.1 模块搜索路径
在导入名为spam的模块时,解释程序先在当前目录中寻找名为“spam.py”的文件,然后从环境变量PYTHONPATH所定义的目录列表中寻找。PYTHONPATH的用法和可执行文件的搜索路径PATH用法相同,都是一个目录列表。当PYTHONPATH未设置的时候,或者文件仍找不到,则搜索继续在安装时设定的缺省路径搜索,在Unix中,这通常是“.:/usr/local/lib/python” 。
实际上,模块是按变量sys.path指定的路径搜索的,此变量在解释程序启动时初始化为包含输入脚本的目录(或当前路径),PYTHONPATH和安装缺省路径。这样,用户可以通过修改sys.path 来修改和替换模块搜索路径。参见后面关于标准模块的一节。
6.1.2 “编译”的Python文件
为了提高调用许多标准模块的小程序的启动时间,一个重要的措施是,如果在找到“spam.py ”的目录中存在一个名为“spam.pyc”的文件,就认为此文件包含了模块spam的一个所谓“ 字节编译”版本。用于生成“spam.pyc”的“spam.py”的修改时间被记入了“spam.pyc”中,如果记录的修改时间与现在文件的时间不相符的话就忽略编译文件。
一般不需要自己生成“spam.pyc”这样的编译文件。每当“spam.py”成功编译后解释程序就尝试写编译版本“spam.pyc”,如果不可写也不会出错;如果因为某种原因此文件没有写完则生成的“spam.pyc”被识别为不完整的而被忽略。编译文件“spam.pyc”的格式是不依赖于平台的,所以不同结构的机器可以共享Python模块目录。
下面是对专家的一些窍门: 
如果Python解释程序是以-O标志启动的,将生成优化的编译代码,保存在“.pyo”文件中。目前优化不是很多,现在只是去掉assert语句和SET_LINENO指令。使用了-O标志时,所有字节码都是优化的,“.pyc”文件被忽略,“.py”文件被编译为优化的字节码。
给Python解释程序两个优化标志(-OO)产生的优化代码有时会导致程序运行不正常。目前双重优化只从字节码中删除了__doc__字符串,使得“.pyo”文件较小。有些程序可能是依赖于文档字符串的,所以只有在确知不会有问题时才可以使用这样的优化。
从“.pyc”或“.pyo”读入的程序并不能比从“.py”读入的运行更快,它们只是调入速度更快一些。
如果一个程序是用在命令行指定脚本文件名的方式运行的,脚本的字节码不会写入“.pyc ”或“.pyo”文件。所以如果把程序的主要代码都移入一个模块,脚本中只剩下导入该模块的引导程序则可以略微缩短脚本的启动时间。
可以有叫做“spam.pyc”(当用了-O标志时为“spam.pyo”)的文件而没有对应的源文件“spam.py”。这可以用来分发一个比较难反编译的Python代码库。
模块compileall可以把一个目录中所有模块编译为“.pyc”文件(指定了-O选项时编译为“.pyo”文件)。
6.2 标准模块
Python带有一个标准模块库,在另一个文档《Python库参考》中进行了描述。一些模块直接编入了解释程序中,这些模块不是语言的核心,为了运行效率或者为了提供对于系统调用这样的系统底层功能而编入了解释程序中。提供那些模块是编译时的选择,例如,amoeba模块只在提供amoeba底层指令的系统中才能提供。
有一个模块值得特别重视:sys模块,每一个Python解释程序中都编译入了这个模块。变量sys.ps1和sys.ps2定义了交互运行时的初始提示和续行提示。
>>> import sys
>>> sys.ps1
'>>> '
>>> sys.ps2
'... '
>>> sys.ps1 = 'C> '
C> print 'Yuck!'
Yuck!
C>

这两个变量只在解释程序以交互方式运行时才有定义。 
变量sys.path是一个字符串列表,由它确定解释程序的模块搜索路径。它被初始化为环境变量PYTHONPATH所指定的缺省路径,环境变量没有定义时初始化为安装时的缺省路径。可以用标准的列表操作修改这个搜索路径,例如:
>>> import sys
>>> sys.path.append('/ufs/guido/lib/python')

6.3 dir()函数
内置函数dir()用于列出一个模块所定义的名字,它返回一个字符串列表: 
>>> import fibo, sys
>>> dir(fibo)
['__name__', 'fib', 'fib2']
>>> dir(sys)
['__name__', 'argv', 'builtin_module_names', 'copyright', 'exit',
'maxint', 'modules', 'path', 'ps1', 'ps2', 'setprofile', 'settrace',
'stderr', 'stdin', 'stdout', 'version']

没有自变量时,dir()列出当前定义的名字。
>>> a = [1, 2, 3, 4, 5]
>>> import fibo, sys
>>> fib = fibo.fib
>>> dir()
['__name__', 'a', 'fib', 'fibo', 'sys']

注意dir()列出了所有各类名字:变量名、模块名、函数名,等等。dir()不会列出内置函数、变量的名字。要想列出内置名字的话需要使用标准模块__builtin__:
>>> import __builtin__
>>> dir(__builtin__)
['AccessError', 'AttributeError', 'ConflictError', 'EOFError', 'IOError',
'ImportError', 'IndexError', 'KeyError', 'KeyboardInterrupt',
'MemoryError', 'NameError', 'None', 'OverflowError', 'RuntimeError',
'SyntaxError', 'SystemError', 'SystemExit', 'TypeError', 'ValueError',
'ZeroDivisionError', '__name__', 'abs', 'apply', 'chr', 'cmp', 'coerce',
'compile', 'dir', 'divmod', 'eval', 'execfile', 'filter', 'float',
'getattr', 'hasattr', 'hash', 'hex', 'id', 'input', 'int', 'len', 'long',
'map', 'max', 'min', 'oct', 'open', 'ord', 'pow', 'range', 'raw_input',
'reduce', 'reload', 'repr', 'round', 'setattr', 'str', 'type', 'xrange']

6.4 包
Python中可以用“包”来组织Python的模块名字空间,名字引用时可以用“带点的模块名。例如,模块名A.B代表包“A”内名为“B”的子模块。正如使用模块可以使不同模块的作者不用顾虑彼此的全局变量名会冲突,使用带点的模块名可以使多模块包如NumPy和PIL的作者不需要担心彼此的模块名会冲突。
假设你有一系列处理声音文件和声音数据的模块(称为一个“包”)。有许多种不同的声音文件格式(通常用扩展名来识别,如“wav”,“.aiff”,“.au”),所以你可能需要制作并维护一组不断增加的模块来处理不同文件格式的转换。你还可能需要对声音数据进行许多不同的操作(如混音、回响、均衡、产生模拟立体声效果),所以你还需要不断增加模块来执行这些操作。一下是你的程序包的可能的结构(用一个分层文件系统表示):
 
Sound/                          顶层包
      __init__.py               初始化音响包
      Formats/                  用于文件格式转换的子程序包
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      Effects/                  用于音响效果的子程序包
              __init__.py
              echo.py
              surround.py
              reverse.py
              ...
      Filters/                  用于滤波的子程序包
              __init__.py
              equalizer.py
              vocoder.py
              karaoke.py
              ...
 

包目录中的“__init__.py”文件是必须得,用来指示Python把这个目录看成包,这可以防止有相同名字如“string”的子目录掩盖住在搜索路径后面一些出现的模块定义。在最简单的情况下,“__init__.py”可以是一个空文件,它也可以包含初始化包所需的代码,和设置“__all__”变量,这些后面会加以讨论。
包的用户可以从包中导入单独的模块,如: 
    import Sound.Effects.echo
这可以把子模块Sound.Effects.echo导入。要引用它也必须用全名,例如:
Sound.Effects.echo.echofilter(input, output, delay=0.7, atten=4)
导入子模块的另一种办法是:
from Sound.Effects import echo
这同样也导入子模块echo,但调用时不需写包前缀,所以可以用如:
echo.echofilter(input, output, delay=0.7, atten=4)
另外一种写法是直接导入所需的函数或变量:
from Sound.Effects.echo import echofilter
这一次同样是调入了子模块echo,但是使其函数echofilter直接可用:
echofilter(input, output, delay=0.7, atten=4)
注意使用“from 包 import 项”这样的格式时,导入的项可以是包的一个子模块(或子包),也可以是包内定义的其它名字如函数、类、变量。导入语句首先查找包内是否定义了所需的项,如果没有则假设它是一个模块然后调入。如果找不到,结果引起ImportError。
相反的,当使用“import item.subitem.subsubitem”这样的格式时,除最后一个外其它各项都应该是包,最后一项可以是包也可以是模块,不允许是前面一项内部定义的类、函数或变量。
6.4.1 从包中导入*
现在,如果用户写“from Sound.Effects import *”会发生什么情况?理想情况下我们希望这应该扫描文件系统,找到所有包内的子模块并把它们都导入进来。不幸的是这种操作在Mac和Windows平台上不能准确实现,这两种操作系统对文件名的大小写没有准确信息。在这些平台上,不知道名为“ECHO.PY”的文件会作为模块echo、Echo还是ECHO被导入。(例如,Windows 95在显示文件名时总是讨厌地把第一个字母大写)。DOS的8+3文件名限制更是对长模块名造成了有趣的困难。
这个问题的唯一解决办法是由模块作者显式地提供包的索引。引入*的import语句遵循如下规定:如果包的“__init__.py”文件定义了一个名为“__all__”的列表,这个列表就作为从包内导入*时要导入的所有模块的名字表。因此当包的新版本发布时需要包的作者确保这个列表是最新的。包的作者如果认为不需要导入*的话也可以不支持这种用法。例如,文件Sounds/Effects/__init__.py 可以包含如下代码:
 
__all__ = ["echo", "surround", "reverse"]

这意味着from Sound.Effects import *将从Sound包中导入指定的三个子包。  
如果没有定义__all__,则from Sound.Effects import *语句不会导入Sound.Effects包中的所有子模块;此语句只能保证Sound.Effects被导入(可能是执行其初始化代码“__init__.py ”)并导入包中直接定义的名字。这包括由“__init__.py”定义的任何名字和显式导入的子模块名。这也包括模块中已经在前面用import显式地导入的子模块,例如:
 
import Sound.Effects.echo
import Sound.Effects.surround
from Sound.Effects import *
 

在这个例子中,echo和surround模块被导入当前名字空间,因为它们在执行from...import 语句时已定义(在定义了__all__的情况下这一点也是成立的)。
注意用户应尽量避免使用从模块或包中导入*的做法,因为这样经常导致可读性差的代码。尽管如此,在交互运行时可以用导入*的办法节省敲键次数,而且有些模块在设计时就考虑到了这个问题,它们只输出遵循某种约定的名字。注意,from 包 import 特定子模块的用法并没有错,实际上这还是我们推荐的用法,除非程序还需要用到来自其它包的同名的子模块。
6.4.2 包内部引用
子模块常常需要彼此引用。例如,模块surround可能要用到模块echo。事实上,这样的引用十分常见,所以import语句首先从子模块的所在包中寻找要导入的子模块才在标准模块搜索路径查找。所以,模块surround只要写import echo或from echo import echofilter。如果在包含本模块的包中没有找到要导入的模块,import语句将去寻找指定名字的顶级模块。
当包组织成子包时(比如例中的Sound包),没有一种简单的办法可以引用兄弟包中的子模块――必须使用子模块的全名。例如,如果模块Sound.Filters.vocoder要引用Sound.Effects 包中的echo模块,它可以用Sound.Effects import echo。

上一页  [1] [2] 

  • 上一篇文档:
  • 下一篇文档:
  • 网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!)
    | 设为首页 | 加入收藏 | 联系站长 | 版权申明 | 雁过留声 | 会员中心 |