模块

模块

模块

模块:是一个包含PYTHON定义和语句的文件,以.py结尾的文件名就是模块名。

在模块内模块的名字(作为字符串)保存在全局变量__name__中。

例如,我们编写一个名为fibo.py的文件,里面有两个函数:fib(n)和fib2(n)。在python解释器中使用import fibo可以导入这个模块。使用模块名可以访问这两个函数:fibo.fib(1000)。fibo.__name__保存的就是这个模块的名字。

每个模块都有自己的符号表,作为自己的全局符号表,每个模块有也有自己的全局变量。所以模块的作者不用担心自己的全局变量会和其它模块冲突。如果想访问这些变量,可以使用modname.itemname的这种形式访问。

import的一种变形,可以直接把函数直接导入到另一个模块的符号表中:

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

还有一种导入所有名字的方法:

from fibo import *

这种方法导入了除了以下划线(_)开头的所有名字。很多情况不使用这种方法,因为可能导入一些和现有文件冲突的名字。

如果导入的名字后面有一个as关键字,那么as后的名字被直接绑定到这个导入的模块上,如:

import fibo as fib

fib.fib(500)

这个关键子也可以用于带有from形式的版本,如:

from fibo import fib as fibonacci

fibonacci(500)

以脚本的方式执行一个模块

python fibo.py <arguments>

模块中的代码将会被执行,就像导入了该模块一样,而且__name__被设置成”__main__”。这意味着要在代码底部添加:

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

代码。这可以让模块像被从一个叫main的文件中导入了一样。这种方法通常用于测试模块。

模块的搜索目录

当一个被命名为spam的模块被导入时,解释器会先在内建模块中查找这个名字。如果没有找到,之后会在sys.path这个列表中查找。sys.path在如下位置被初始化:

  • 包含在输入脚本(如果没有指定文件时,是当前文件夹)
  • PYTHONPATH
  • 依赖于安装时的默认值

在初始化后,python程序能修改sys.path。

包:是一种python模块名字空间的组织方法,使用“点分模块名”的方法。例如:一个模块的名字A.B,代表在一个名为A的包中包含了一个名为B的子模块。(python中的‘包’就是目录)

假设你想设计一个模块的集合,用于处理声音文件和声音数据。有很多不同的声音格式,所以你可能需要去创建和维护一个不断增加的模块集合,用于在不同格式之间转换。还有很多你想在声音数据上执行的不同的操作,所以这个模块你可能会一直写下去,没有终点。这里有一个可能的结构:

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文件要求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.echofilter(input,output, delay=0.7, atten=4)

再或者直接导入要使用的函数或变量:

from sound.effects.echo import echofilter

这样就可以直接调用该函数:

echofilter(input,output, delay=0.7, atten=4)

注意:当使用from package import item这种形式时,item可以是包内的模块(子包)或者是在模块中定义的一些名字,如:一个函数、类或变量。import语句先测试item是否在’包’中;如果不在,假定它是一个模块并试图加载它。如果没有找到会抛出一个ImportError异常。
当使用import item.subitem.subsubitem这种形式时,除了最后一项,其它必须是’包’;最后一项可以是模块或者是包,但是,不能是一个类、函数或定义的变量。

dir()函数

内建的dir()函数用于找出定义在包中的所有名字,如:

>>> import fibo, sys
>>> dir(fibo)
['__name__', 'fib', 'fib2']

从包中导入*

如果用户输入from sound.effects import *,将会导入‘包’中的有的子模块,不过这可能会耗时很长,而且会有一些不想要的副作用。

解决的办法是为‘包’的作者提供一个明显的包序号。import语句使用如下导入顺序:如果包的__init__.py定义了一个名为__all__的列表,当遇到from package import *时,它处理这个将要被导入的模块名列表。如果__all__没被定义,那么from sound.effects import *语句不会sound.effects的所有子模块导入当前的名字空间;它能只保证包sound.effects已经被导入。考虑下面代码:

import sound.effects.echo
import sound.effects.surround
from sound.effects import *
接下来来可以执行诸如echo.echofilter(input, output, delay=0.7, atten=4)这样的函数。

当执行from...import语句时,定义在sound.effects包中的echo和surround模块被导入到当前的名字空间中。
虽然某些模块仅被设计用来导入,但是使用import *仍然不被推荐。
使用from PACKAGE import SPECIFIC_SUBMODULE这种形式是被推荐使用的,除非要导入两个包的同名子模块。

包内索引

当包具有多层结构时(就像例子中的sound包一样),可以使用绝对导入,索引到子模块。例如,如果模块sound.filters.vocoder需要在sound.effects包中使用echo模块,可以使用from sound.effects import echo导入。
也可以使用相对导入,from module import name这种形式的导入语句。这些导入使用“引导点”指示当前目包和父级包。在surround模块中的例子:

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

注意相对导入是基于当前模块的名字的。因为主模块的名字一直是“__main__”,PYTHON应用程序都是从”__main__”开始使用,所以在主模块中必须一直使用绝对导入。

在多个目录中的包

包支持很多的特殊属性,__path__。在代码执行以前它被初始化成为一个列表,包含了拥有包的__init__.py文件的目录的名字。这个变量可以修改;如果这么做了会影像模块和包的搜索。

发表评论