python 2.7 中文教程-6:模块和包

来源:互联网 发布:windows ce安装软件 编辑:程序博客网 时间:2024/06/06 04:36

模块

退出Python解释器再重新进入,函数和变量等的定义都会丢失。 较大的程序使用文本编辑器编辑的文件作为执行输入更好,即创建脚本。 程序变很长时可将它分割成几个更易于维护的文件。你也可能想在几个程序中使用相同的函数,而不是拷贝代码。

Python可在文件中定义,在脚本或者解释器中使用。这样的文件即模块。模块中的定义可导入到其他模块或主模块中(即脚本顶层或命令行可以访问的变量集)。

模块是包含Python定义和语句的文件。文件名就是模块名加上.py 缀。模块名可以由全局变量name 获取。实例:fibo.py:
 

# Fibonacci numbers moduledef fib(n):    # write Fibonacci series up to n    a, b = 01    while b < n:        print b,        a, b = b, a+b        def fib2(n): # return Fibonacci series up to n    result = []    a, b = 01    while b < n:        result.append(b)        a, b = b, a+b    return result

命令行导入:
 

>>> import fibo

调用
 

>>> fibo.fib(1000)1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987>>> fibo.fib2(100)[1123581321345589]>>> fibo.__name__'fibo'

起别名:
 

>>> fib = fibo.fib>>> fib(500)1 1 2 3 5 8 13 21 34 55 89 144 233 377

深入模块

除了函数定义,模块也可以包含可执行语句。这些语句一般用来初始化模块。它们仅在第一次导入时执行。要强行加载,可以使用reload(modulename)。

每个模块都有自己私有的符号表,模块内所有的函数将其作为全局符号表使用。 因此模块内部使用全局变量不会和用户的全局变量发生冲突。通过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, fib2>>> fib(500)1 1 2 3 5 8 13 21 34 55 89 144 233 377

还可以导入所有非私有的定义:
 

>>> from fibo import *>>> fib(500)1 1 2 3 5 8 13 21 34 55 89 144 233 377

这样可以导入所有非下划线开头的命名。因为可读性不好,一般不推荐使用。但是可以用不过来保存交付式会话。
需要注意的是在实践中往往不鼓励从一个模块或包中使用 * 导入所有,因为这样会让代码变得很难读。不过在交互式会话中很方便。

 

用脚本的方式执行模块


执行方式:
 

python fibo.py <arguments>

此时name 设置为"main",这样根据是否是主文件判断执行。比如在fibo.py添加内容:
 

if __name__ == "__main__":    import sys    fib(int(sys.argv[1]))

这样导入的时候不会执行上述内容,但是作为主文件执行时会执行。
 

python fibo.py 501 1 2 3 5 8 13 21 34>>> import fibo>>>

模块的搜索路径

导入spam模块时,解释器先在内置模块中寻找,然后是sys.path。sys.path定义的位置如下:

  • 当前目录

  • PYTHONPATH变量(类似shell的PATH)

  • Python默认的安装目录
     

初始化之后可以修改sys.path。注意因为当前的目录的优先级较高,尽量不要存在和其他模块名字相同的文件。这是个常见错误。

 

“编译的” Python 文件

pyc文件可以加快大量引用标准模块的短程序的加载速度法。它会被视为源码的预“编译”( byte-compiled,二进制编译)版本。python的字节码文件后缀pyc,以修改时间做为版本号,如果文件没有修改,会直接调用pyc文件而不是py文件,反之调用py文件并生成pyc文件,pyc文件如果生成失败也没有关系。PHP需要每次都编译,所以python的效率会明显比PHP高。

通常你不需要为创建 spam.pyc 文件做任何工作。一旦 spam.py 成功编译,就会尝试生成对应版本的 spam.pyc 。如果有任何原因导致写入不成功,生成的 spam.pyc 文件就会视为无效,随后即被忽略。 spam.pyc 文件的内容是平台独立的,所以 Python 模块目录可以在不同架构的机器之间共享。

部分高级技巧:

    以 -O 参数调用Python 解释器时会生成优化代码并保存在.pyo文件中。目前只是删除了assert语句。pyo文件的优先级高过pyc。
    -OO比-O更深一层,删除了文档字符串。因为某些程序依赖于这些变量的可用性,但个别情况下会导致程序不能执行。
    .pyc和.pyo只能提高加载速度,不能提高执行速度。
    指定文件名在命令行执行时,不会生成.pyc或.pyo文件。所以import动作放入专门的导入模块更好。当然也可以手工创建.pyc 或 .pyo 文件。
    可以只发布.pyc或.pyo文件而不发布py文件,加大逆向工程的难度。
    compileall 模块为指定目录中的所有模块创建 .pyc 文件(或者使用-O参数创建 .pyo 文件)。

标准模块

Python的文档参见标准模块库(Python Library Reference)。一些标准模块库模块为了效率或者访问系统原语(比如系统调用)内置于解释器之中,但是并不是python的核心部分,注意有些模块可能不是跨平台的。例winreg 模块在 Windows系统上才有。有一个具体的模块值得注意: sys的变量 sys.ps1 和 sys.ps2 定义了主提示符和辅助提示符字符串:
 

>>> import sys>>> sys.ps1'>>> '>>> sys.ps2'... '>>> sys.ps1 = 'Python> 'Python> print 'Yuck!'Yuck!Python>

这两个变量只在交互模式下有意义。

变量sys.path是解释器模块搜索路径。它由环境变量 PYTHONPATH 初始化,如果没有设定PYTHONPATH ,就由内置的默认值初始化。你可以用标准的列表操作修改它:
 

>>> import sys>>> sys.path.append('/ufs/guido/lib/python')

dir()函数

内置函数 dir() 用于查看模块的定义,它返回一个排序的字符串列表:
 

>>> import fibo, sys>>> dir(fibo)['__name__''fib''fib2']>>> dir(sys)  ['__displayhook__''__doc__''__excepthook__''__name__''__package__''__stderr__''__stdin__''__stdout__''_clear_type_cache''_current_frames''_getframe''_mercurial''api_version''argv''builtin_module_names''byteorder''call_tracing''callstats''copyright''displayhook''dont_write_bytecode''exc_clear''exc_info''exc_traceback''exc_type''exc_value''excepthook''exec_prefix''executable''exit''flags''float_info''float_repr_style''getcheckinterval''getdefaultencoding''getdlopenflags''getfilesystemencoding''getobjects''getprofile''getrecursionlimit''getrefcount''getsizeof''gettotalrefcount''gettrace''hexversion''long_info''maxint''maxsize''maxunicode''meta_path''modules''path''path_hooks''path_importer_cache''platform''prefix''ps1''py3kwarning''setcheckinterval''setdlopenflags''setprofile''setrecursionlimit''settrace''stderr''stdin''stdout''subversion''version''version_info''warnoptions']

无参数调用时, dir()函数返回当前定义名字列表:
 

>>> a = [12345]>>> import fibo>>> fib = fibo.fib>>> dir()['__builtins__''__name__''__package__''a''fib''fibo''sys']

注意该列表列出了所有类型的名称:变量,模块,函数等。 dir() 不会列出内置函数和变量名。如果你想列出这些内容,查看builtin 就好:
 

>>> import __builtin__>>> dir(__builtin__)  ['ArithmeticError''AssertionError''AttributeError''BaseException''BufferError''BytesWarning''DeprecationWarning''EOFError''Ellipsis''EnvironmentError''Exception''False''FloatingPointError''FutureWarning''GeneratorExit''IOError''ImportError''ImportWarning''IndentationError''IndexError''KeyError''KeyboardInterrupt''LookupError''MemoryError''NameError''None''NotImplemented''NotImplementedError''OSError''OverflowError''PendingDeprecationWarning''ReferenceError''RuntimeError''RuntimeWarning''StandardError''StopIteration''SyntaxError''SyntaxWarning''SystemError''SystemExit''TabError''True''TypeError''UnboundLocalError''UnicodeDecodeError''UnicodeEncodeError''UnicodeError''UnicodeTranslateError''UnicodeWarning''UserWarning''ValueError''Warning''ZeroDivisionError''_''__debug__''__doc__''__import__''__name__''__package__''abs''all''any''apply''basestring''bin''bool''buffer''bytearray''bytes''callable''chr''classmethod''cmp''coerce''compile''complex''copyright''credits''delattr''dict''dir''divmod''enumerate''eval''execfile''exit''file''filter''float''format''frozenset''getattr''globals''hasattr''hash''help''hex''id''input''int''intern''isinstance''issubclass''iter''len''license''list''locals''long''map''max''memoryview''min''next''object''oct''open''ord''pow''print''property''quit''range''raw_input''reduce''reload''repr''reversed''round''set''setattr''slice''sorted''staticmethod''str''sum''super''tuple''type''unichr''unicode''vars''xrange''zip']

包是使用“圆点模块名”格式结构化python模块命名空间的方法,例如A.B表示A模块中包含了B子模块。这样可以避免多个模块之间的命名冲突。

假设你现在想要设计一个模块集(包)来统一处理声音文件和声音数据。存在几种不同的声音格式(通常由它们的扩展名来标识,例如:.wav, .aiff, .au)。为了处理不同类型的文件格式之间转换,你需要维护不断增长的模块集合。还要对声音数据做很多不同的操作(例如混音,添加回声,均衡,艺术效果),所以需要大量模块,结构如下:
 

sound/                          Top-level package      __init__.py               Initialize the sound package      formats/                  Subpackage for file format conversions              __init__.py              wavread.py              wavwrite.py              aiffread.py              aiffwrite.py              auread.py              auwrite.py              ...      effects/                  Subpackage for sound effects              __init__.py              echo.py              surround.py              reverse.py              ...      filters/                  Subpackage for filters              __init__.py              equalizer.py              vocoder.py              karaoke.py              ...

导入包时,Python通过sys.path查找对应子目录。

包目录中必须包含 init.py 文件,这样可以避免普通目录名覆盖模块名。__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.echofilter(input, output, delay=0.7, atten=4)


类似的可以直接导入函数或者变量:

from sound.effects.echo import echofilterechofilter(input, output, delay=0.7, atten=4)

注意from package import item中的item既可是包中的子模块(包),也可以是包中定义的其它名字,像函数、类或变量。import先检查包中是否有,如果没有会当做模块来加载,再找不到它,会引发ImportError 异常。

相反import item.subitem.subsubitem中每个子项必须是包,最后的子项可以是包或模块,但不能是类、函数或变量等。

import *

看上去from sound.effects import *会导入所有子模块,花掉很长时间。不过包中定义__all__列表可以避免全部导入。比如effects目录的init.py文件如果定义了__all__, 上述命令就只会导入该列表对应的子模块。

__all__ = ["echo""surround""reverse"]

如果没有定义__all__, from sound.effects import * 语句不会从sound.effects 包中导入所有的子模块,它只保证导入了sound.effects,执行了_init__.py
 

import sound.effects.echoimport sound.effects.surroundfrom sound.effects import *

import * 不是建议的写法。推荐from Package import specific_submodule,但是注意不要有重名。

 

包内引用

    绝对路径:

 from sound.effects import echo


    相对路径:

    from . import echo    from .. import formats    from ..filters import equalizer

需要注意所有导入都基于当前模块的命名,通常为 "__main__",主模块应该总是用绝对路径。

多目录包

包的特殊属性__path__指定了包中包含了init.py文件的目录,方便扩展,但是很少使用

1 0