[Python]模块、包

1. 模块与import语句

任何Python源文件都能以模块的形式使用,例如:

目前创新互联已为成百上千家的企业提供了网站建设、域名、虚拟主机绵阳服务器托管、企业网站设计、西峰网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。

# spam.py
a = 37
def foo():
    print("I'm foo and a is %s" % a)
def bar():
    print("I'm bar and I'm calling foo")
class Spam(object):
    def grok(self):
        print("I'm Spam.grok")

要以模块的形式加载这段代码,可以使用import spam语句。首次使用import加载模块时,它将做3件事:
1) 创建新的命名空间,用作在相应源文件中定义的所有对象的容器。
2) 在新创建的命名空间中执行模块中包含的代码。
3) 在调用函数中创建名称为引用模块命名空间,这个名称与模块的名称相匹配,例如:

import spam
x = spam.a
spam.foo()
s = spam.Spam()
s.grok()

import执行已加载源的文件中的所有语句。要导入多个模块,可以为import提供逗号分隔的模块名称列表,例如:

import socket, os, re

用于引用模块的名称可以使用as限定符进行更改,例如:

import spam as sp
import socket as net
sp.foo()
sp.bar()
net.gethostname()

import语句可以出现在程序中的任何位置,但是每个模块中的代码仅加载和执行一次。
 

2. 从模块导入选定符号

from语句用于将模块中的具体定义加载到当前命名空间中。from语句相当于import,但它不会创建一个名称来引用新创建的模块命名空间,而是将对模块中定义的一个或多个对象的引用放到当前命名空间中。例如:

from spam import foo
foo()
spam.foo() # NameError

from语句还会接受用逗号分隔的对象名称列表,例如:

from spam import(foo, bar, Spam)

另外,as限定符可用于重命名使用from导入的具体对象,例如:

from spam import Spam as Sp
s = Sp()

"*"星号通配符也可用于加载模块中的所有定义,但以下划线开头的定义除外,例如:

from spam import *

form module import 语句只能在模块最顶层使用。通过定义列表__all__,模块可以精确控制from spam import \导入的名称集合,例如:

# spam.py
__all__ = ['bar', 'spam']

使用from导入形式导入定义不会更改定义的作用域规则。函数的全局命名空间始终是定义该函数的模块,而不是将函数导入并调用该函数的命名空间。
 

3. 模块搜索路径

加载模块时,解释器在sys.path路径中搜索字典列表。sys.path中的第一个条目通常是空字符串,表示当前正在使用的字典。sys.path中的其他条目可能包含字典名称、.zip归档文件和.egg文件。各个条目在sys.path中列出的顺序决顺决定了加载模块时的搜索顺序。
可以将一组模块打包为一个文件,设想创建两个模块foo.py和bar.py,并将它们放在一个名为mymodules.zip的zip文件中,就可以以如下方式添加到搜索路径:

import sys
sys.path.append("mymodules.zip")
import foo, bar

zip文件可以与常规路径名称组件混合使用,例如:

sys.path.append("/tmp/modules.zip/lib/python")

从zip中导入需要注意一些限制。首先,只能从归档文件中导入.py、.pyw、.pyc和.pyo文件。而且,从归档文件加载.py文件时,Python不会建.pyc和.pyo文件,这会导致加载模块时的性能下降。
 

4. 模块加载和编译

使用import加载的模块实际上可分为4个通用类别:
1) 使用Python编写的代码
2) 已被编译为共享库或DLL的C或C++扩展
3) 包含一组模块的包
4) 使用C编写并链接到Python解释器的内置模块
查看编块(以foo为例)时,解释器在sys.path下的每个目录中搜索以下文件(按搜索顺序列出):
1) 目录foo,它定义了一个包
2) foo.pyd、foo.so、foomodule.so或foomodule.dll(已编译的扩展)
3) foo.pyo(只适用于使用了-O或-OO选项时)
4) foo.pyc
5) foo.py(Windows上还会查找.pyw文件)
对于.py文件,首次导入模块时,它会被编译为字节码并作为.pyc文件写回磁盘。在后续导入中,解释器将加载这段预编译的字节码,除非.py文件的修改日期要更新一些。.pyc文件与解释器的-O选项结合,文件已删除了行号、断言和其他调试信息的字节码。如果指定-OO选项,还会从文件中删除文档字符串。
只有使用import语句才能将文件自动编译为.pyc和.pyo文件。另外,如果包含模块的.py文件的目录不允许写入,将不会创建这些文件。解释器的-B选项也可以禁止生成这些文件。
如果存在.pyc和.pyo文件,则可以没有相应的.py文件。但即使没有提供源文件,仍然可以检查并找到大量细节。并且,为某个Python版本生成的.pyc文件可能不适用于其他的Python版本。
 

5. 包

包可用于将一组模块分组到一个常见的包名称下,这有助于解决不同应用程序中使用的模块名称之间的命名空间冲突问题。包是通过使用与其相同的名称创建目录,并在该目录中创建文件__init__.py来创建的。可以将如下形式组织一个包:

Graphics/
    __init__.py
    Primitive/
        __init__.py
        lines.py
        fill.py
        text.py
    Graph3d/
        __init__.py
        plot2d.py
    Graph4d/
        __init__.py
        plot3d.py
    png.py
    tiff.py
    jpeg.py

import语句用于通过多种方式从包中加载模块:
1) import Graphics.Primitive.fill
2) from Graphics.Primitive import fill
3) from Graphics.Primitive.fill import floodfill
只要第一次导入包中的任何部分,就会执行__init__.py中的代码。这个文件可以为空。在import语句执行期间,遇到的所有__init__.py文件都会执行。因此,之前示例中的import Graphics.Primitive.fill语句将会首先执行Graphics目录中的__init__.py文件,然后执行Primitive目录中的__init__.py文件。
处理以下这条语句时需注意:

from Graphics.Primitive import *

使用该语句希望将与某个包相关联的所有子模块导入到当前命名空间中,但是该语句只会导入在Primitive目录的__init__.py文件中定义的所有名称。这种行为可以通过定义列表__all__来修改,例如:

# Graphics/Primitive/__init__.py
__all__ = ["lines", "text", "fill"]

如果想要导入同一个包中的其他子模块时,可以使用相对导入,例如:

# fill.py
from . import lines

相对导入也可以加载同一个包的不同目录中包含的子模块,例如:

# plot2d.py
from ..Primitives import lines

相对导入只能命名用from module import symbol形式的导入语句来指定。因此import ..Primitives.lines或import .lines这样的语句在语法上是不对的。
最后,Python导入一个包时,它将定义特殊变量__path__,该变量包含一个目录列表,查找包的子模块时将搜索这个列表。__path__可通过__init__.py文件中包含的代码访问,最初包含的一项具有包的目录包称。如果有必要,包可以向__path__列表提供更多目录,更改查找子模块时使用的搜索路径。


网页标题:[Python]模块、包
文章来源:http://azwzsj.com/article/gjosoi.html