用户登录  |  用户注册
首 页商业源码原创产品编程论坛
当前位置:PB创新网文章中心编程技巧编程其他

Python编程入门(5)

减小字体 增大字体 作者:佚名  来源:本站整理  发布时间:2009-03-16 19:39:39

第六章 模块

如果退出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.echoimport Sound.Effects.surroundfrom 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包),没有一种简单的办法可以引用兄弟包中的子模块?D?D必须使用子模块的全名。例如,如果模块Sound.Filters.vocoder要引用Sound.Effects 包中的echo模块,它可以用Sound.Effects import echo。


Tags:

作者:佚名

文章评论评论内容只代表网友观点,与本站立场无关!

   评论摘要(共 0 条,得分 0 分,平均 0 分) 查看完整评论
PB创新网ourmis.com】Copyright © 2000-2009 . All Rights Reserved .
页面执行时间:25,531.25000 毫秒
Email:ourmis@126.com QQ:2322888 蜀ICP备05006790号