模块和包

 

import 导入机制

  

1、标准 importpython

  Python中全部加载到内存的模块都存放在 sys.modules 列表中。缓存

  当import xxx 一个模块时,首先去 sys.modules 列表中查找是否已经加载了 xxx模块,bash

    若是已经加载了,就把模块名字xxx 加入到正在调用 import xxx 模块的 Local 名字空间中。app

    若是没有加载,则从 sys.path 目录中按照模块名称查找模块文件,模块能够是py、pyc、pyd,找到后将模块载入内存,并加到 sys.modules 中,并将名称导入到当前的 Local 名字空间。函数

  一个模块不会重复导入,多个不一样的模块均可以用 import 引入同一个模块到本身的 Local 名字空间中。编码

  import 只能导入模块,不能导入模块中的 对象(函数、类、变量等);url

    例如:模块 A 中 有一个函数 getName(),另外一个模块不能经过 import A.getName 将 getName 导入到本模块,只能用 from A import getName。spa

 

2、嵌套 importcode

  (1)顺序嵌套。orm

  本模块导入A模块(import A),A中又 import B,B模块又 import 其余模块 ....

  本模块 import A 后,本模块只能访问A,不能访问 模块B 以及 其余模块,即便 B模块已经加载在内存中,若是访问还要在本模块中 import B。

  (2)循环嵌套。

  文件[ A.py ]

    from B import D

    class C: pass

  文件[ B.py ]

    from A import C

    class D: pass

  当执行文件 A.py 到 import D时会报错,可是将文件[ A.py ],修改成 import B 便可。

  当执行语句 from B import D 时,分为如下步骤:

      1)在 sys.modules 中查找符号 “B”

      2)若是符号 “B” 存在,则直接得到 符号 “B” 对应的 module 对象。

      从 <module B> 对象的 __dict__ 中得到符号 “D” 对应的对象,若是 “D” 不存在,则抛出异常。

      3)若是符号 “B” 不存在,则建立一个新的 module 对象, <module B> ,此时 <module B> 的 __dict__ 为空,

      执行[ B.py ] 中的表达式,填充  <module B> 的 __dict__ ,

      从 <module B> 对象的 __dict__ 中得到符号 “D” 对应的对象,若是 “D” 不存在,则抛出异常。

  例子中的执行顺序为:

  (1)先执行 A.py 中的 from B import D,因为是执行的 python A.py ,因此 sys.modules 中并无 <module B>,首先为 B.py 建立一个module对象( <module B> ),

      此时,module 对象是空的,而后解析执行 B.py。

  (2)转去执行 B.py 中的 from A import C,首先检查 sys.modules 是否存在 <module A>,因为此时尚未缓存 <module A> ,因此相似的,Python内部为 A.py 建立一个 module 对象( <module A>),而后一样转去执行 A.py中的语句。

  (3)再次执行 A.py 中的 from B import D 时,因为在第(1)步建立的 <module  B> 对象,已经缓存在 sys.modules 中,因此直接就获得了 <module B>,可是<module B>,依旧是一个空对象,当从<module B> 获取 符号 “D” 时,就会抛出异常。若是这里是 import B,因为符号 “B” 在 sys.modules 中已经存在,因此不会抛出异常。

 

3、包 import

  只要一个文件夹下面有 __init__.py 文件,这个文件夹就能够看作包。

  包的导入过程和模块基本一致,只是导入包的过程当中,先执行包目录下的 __init__.py。

  若是只是单纯的导入包,而包的 __init__.py 中没有其余初始化的操做,那么此包下面的模块是不会自动导入的。

 

 

1、把模块按层次结构组织成包

  建立一个软件包在目录中定义一个__init__.py文件。

  __init__.py文件的目的就是包含可选的初始化代码,当遇到软件包中不一样层次的模块时会触发运行。

  对于导入语句:>>> import graphics.formats.jpg

  文件graphics/__init__.py 和 graphics/formats/__init__.py 都会在导入文件graphics/formats/jpg.py以前优先获得导入!

  大部分状况下,把__init__.py文件留空也是能够的。可是,在某些特定的状况下__init__.py文件可用来加载子模块。

# graphics/formats/__init__.py

from . import jpg
from . import png

 

2、对全部符号的导入进行控制

  使用>>> from module import * 语句时,在模块中定义__all__,那么只有显式列出的符号名才会被导出。

  若是将__all__定义成一个空列表,那么任何符号都不会被导出。

  若是__all__包含有未定义的名称,那么在执行import语句时会产生一个AttributeError异常。

  __all__ = [ 'spam', 'grok' ]

 

3、用相对名称来导入包中的子模块

  要在软件包的子模块中导入同一个包中其余的子模块,请使用相对名称来导入。

mypackage/
    __init__.py
    A/
        __init__.py
        spam.py
        grok.py
    B/
        __init__.py
        bar.py

  若是模块mypackage.A.spam要导入位于同一个目录中的模块grok,应该使用 

  >>> from . import grok

  若是模块mypackage.A.spam要导入位于不一样目录中的模块 B.bar,应该使用 

  >>> from ..B import bar

  >>> from mypacksge.A import grok

  使用绝对名称的缺点在于这么作会将最顶层的包名称硬编码到源代码中,这使得代码更加脆弱,若是想从新组织一下结构会比较困难。

  ▲ 一切包的相对导入都是在主程序所在目录之下进行的,不能导入它的上一级目录中的包。

  ▲ 位于脚本顶层的模块不能使用相对导入。

  ▲ 包的某个部分是直接以脚本的形式执行的,这种状况下也不能使用相对导入。

 

4、将模块分解成多个文件

  能够经过将模块转换为包的方式将模块分解成多个单独的文件。

# mymodule.py

class A:
    def spam(self):
        print('A.spam')

class B(A):
    def bar(self):
        print('B.bar')

  把mymodule.py分解为两个文件,每一个文件包含一个类的定义。

mymodule/
    __init__.py
    a.py
    b.py

# a.py
class A:
    def spam(self):
        print('A.spam')

# b.py
from .a import A
class B(A):
    def bar(self):
        print('B.bar')

# __init__.py
from .a import A
from .b import B

  建立一个包目录,并经过__init__.py文件将各个部分粘合在一块儿。

  引入惰性导入的概念。__init__.py文件一次性将全部须要的组件都导入进来。

  若是只但愿在实际须要的时候才加载那些组件。为了实现这个目的,下面对__init__.py文件作了修改:

# __init__.py
def A():
    from .a import A
    return A()

def B():
    from .b import B
    return B()

  惰性加载的主要缺点在于会破坏继承和类型检查机制。

  >>> if isinstance(x, mymodule.A):   # Error

  >>> if isinstance(x, mymodule.a.A):  # OK

 

5、让各个目录下的代码在统一的命名空间下导入

  使各个单独的目录统一在一个公共的命名空间下,能够把代码像普通的Python包那样进行组织。

  可是对于打算合并在一块儿的组件,这些目录中的__init__.py文件则须要忽略。

foo-package/
    spam/
        blah.py

bar-package/
    spam/
        grok.py

  spam同来做为公共的命名空间。注意这两个目录中都没有出现__init__.py文件。

  将foo-package和bar-package都添加到Python的模块查询路径中,而后尝试作一些导入操做。

  >>> sys.path.extend(['foo-package', 'bar-package'])

  >>> import spam.blah

  >>> import spam.grok

  两个不一样的包目录合并在了一块儿,能够随意导入spam.blah或者spam.grok。

原理:

  使用了命名空间包的特性。

  命名空间包是一种特殊的包,用来合并不一样目录下的代码,把他们放在统一的命名空间之下进行管理。

  关键在于确保统一命名空间的顶层目录不包含__init__.py文件。 

  命名空间包的一个重要特性是任何人均可以用本身的代码来扩展命名空间中的内容。

  每一个包都在__file__模块中保存了全路径。

  若是缺乏__file__属性,这个包就是命名空间。

 

6、从新加载模块

  从新加载一个已经加载过了的模块,使用imp.reload()

  >>> import spam

  >>> import imp

  >>> imp.reload(spam)

  可是,reload()操做不会更新 from module import xxx 这样的语句导入的定义。

 

7、让目录或zip文件称为可运行的脚本

  建立一个目录或zip文件,在其中添加一个__main__.py,这是一种打包规模较大的Python应用程序的可行方法。

 

   

8、读取包中的数据文件

  包的结构:

mypackage/
    __init__.py
    somedata.dat
    spam.py

  >>> import pkgutil

  >>> data = pkgutil.get_data(__package__, 'somedata.dat')

  get_data()的第一个参数是包含有包名的字符串。咱们能够直接提供这个字符串,或者使用__package__这个特殊变量。

  第二个参数是要获取的文件相对于包的名称。

 

9、添加目录到sys.path中

  建立一个.pth文件,将目录写出来:

# myapplication.pth
/some/dir
/other/dir

  这个.pth文件须要放在Python的其中一个site-packages目录中,通常来讲位于/usr/local/lib/python3.3/site-packages。

  在解释器启动的时候,只要.pth文件中列出的目录存在于文件系统中,那么它们就会被添加到sys.path中。

 

10、使用字符串中给定的名称来导入模块

  当模块或包的名称以字符串的形式给出时,可使用importlib.import_module()函数来手动导入这个模块。

  >>> import importlib

  >>> math = importlib.import_module('math')

  >>> math.sin(2)

  导入包 from . import b 的用法:

  >>> b = importlib.import_module('.b', __package__)

 

 

十3、安装只为本身所用的包

  Python有一个用户级的安装目录,一般位于~./local/lib/python3.3/site-packages这样的目录下。

  python3 setup.py install -user  或者

  pip install --user packagename

  用户级的site-package目录一般会在sys.path中出现,并且位于系统级的site-package目录以前。

  所以,采用这种技术安装的包比已经安装到系统中的包优先级要高

 

十4、建立新的Python环境

  经过pyvenv命令建立一个新的虚拟环境。

  bash % pyvenv Spam

  Spam目录下会建立出 bin、include、lib、pyvene.cfg目录,而且在bin目录下有一个可以使用的Python解释器。

  bash % Spam/bin/python3

  建立虚拟环境大部分缘由都是为了安装和管理第三方的包。

  下一步一般是安装一个包管理器,distribute或者pip

  若是想将已经安装过的包引入,使其做为虚拟环境的一部分,那么可使用--system-site-packages来建立虚拟环境。

  bash % pyvenv --system-site-packages Spam

 

十5、发布自定义的包

  把编写的库分发给其余人使用。

  典型的程序库结构:

projectname/
    README.txt
    Doc/
        documentation.txt
    projectname/
        __init__.py
        foo.py
        bar.py
        utils/
            __init__.py
            spam.py
            grok.py
    examples/
        hellworld.py

  首先编写一个setup.py文件。

from distutils.core import setup

setup(name='projectname',
  varsion='1.0',
  author='Your Name',
  author_email='you@youaddress.com',
  url='http://www.you.com/projectname',
  packages=['projectname', 'projectname.utils']               
)

  须要手动列出包中的每个子目录:packages=['projectname', 'projectname.utils']

  接下来建立一个MANIFEST.in文件。列出各类但愿包含在包中的非源代码文件:

# MANIFEST.in
include *.txt
recursive-include examples *
recursive-include Doc *

  确保setup.py和MANIFEST.in文件位于包的顶层目录。

  % bash python3 setup.py sdist

  系统会建立出projectname-1.0.zip或者projectname-1.0.tar.gz这样的文件。

相关文章
相关标签/搜索