zoukankan      html  css  js  c++  java
  • Python 模块与包


    一、构建一个模块的层级包

    问题

    将代码组织成由很多分层模块构成的包。


    解决方案

    封装成包很简单。在文件系统上组织你的代码,并确保每个目录都定义一个 __init__.py 文件。例如:

    graphics/
    	__init__.py
        primitive/
        	__init__.py
            line.py
            fill.py
            text.py
    	formats/
        	__init__.py
            png.py
            jpg.py
    

    这样就可以执行各种 import 语句。如下:

    import graphics.primitive.line
    from graphics.primitive import line
    import graphics.formats.jpg as jpg
    

    讨论

    文件 __init__.py 的目的是要包含不同运行级别的包的初始代码

    举个例子,如果执行了语句 import graphics ,文件 graphics/__init__.py 将被导入,建立 graphics命名空间的内容。

    import graphics.format.jpg 这样导入,文件 graphics/__init__.py 和 文件 graphics/formats/__init__.py 会在 graphics/formats/jpg.py 之前导入。


    绝大部分让 __init__.py 空着就好。但有些情况可能包含代码。

    # graphics/formats/__init__.py
    from . import jpg
    from . import png
    

    像这样的文件,用户可以通过 import grahpics.formats 代替 import graphics.formats.jpgimport graphics.formats.png




    二、模块的全部导入

    问题

    使用 from module import * 时,对从模块或包导出的符号进行精确控制。


    解决方案

    在模块中定义一个变量 __all__ 来列出要导出的内容。

    # somemodule.py
    def spam():
        pass
    
    def grok():
        pass
    
    blah = 42
    # 只导出'spam'和'grok'
    __all__ = ['spam', 'grok']
    

    讨论

    尽管强烈反对使用 from module import *,但是在定义了大量变量名的时候还是会使用。因为这样的导入,将导入所有不以下划线开头的。

    如果将 __all__ 定义为空列表,没有东西将被导入。

    如果 __all__包含未定义的名字,引起 AttributeError(属性错误)。




    三、模块的相对路径导入

    问题

    将代码组织成包,用 import 语句从另一个包中导入子模块。


    解决方案

    使用包的相对导入。将一个模块导入同一个包的另一个模块。

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

    模块 mypackage.A.spam 要导入同目录下的 grok

    # mypackage/A/spam.py
    from . import grok
    

    模块 mypackage.A.spam 要导入不同目录下的 B.bar

    # mypackage/A/spam.py
    from ..B import bar
    

    . 为当前目录,..B 为目录 ../B。这种语法只适用于 import。

    两个 import 语句都没包含顶层包名,而是使用了 spam.py 的相对路径。


    讨论

    在包内,既可以使用相对路径,也可以使用绝对路径。

    # mypackage/A/spam.py
    from mypackage.A import grok	# 正确
    from . import grok	# 正确
    import grok			# 错误
    

    绝对路径的缺点是将顶层包名硬编码到你的源码中。

    相对导入只适用于包中的模块。而在顶层脚本的简单模块,它们不起作用。如果包的部分被作为脚本直接执行,它们将不起作用。

    % python mypackage/A/spam.py	# 相对导入失败
    

    如果使用 python -m 执行先前脚本,相对导入正确运行。

    % python -m mypackage/A/spam.py	# 相对导入成功
    



    四、将模块分割成多个文件

    问题

    将一个模块分割成多个文件。


    解决方案

    程序模块通过变成包来分割成多个独立的文件。

    # mymodule.py
    class A:
        def spam(self):
            print('A.spam')
          
    class B:
        def bar(self):
            print('B.bar')
    

    如果想将 mymodule.py 分为两个文件,每个定义一个类。


    首先用 mymodule 目录来替换文件 mymodule.py。这个目录下,创建文件:

    mymodule/
    	__init__.py
        a.py
        b.py
    

    a.py 文件中插入代码:

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

    b.py 文件中插入代码:

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

    最后,在 __init__.py 中,将2个文件粘合在一起:

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

    按上述步骤,所产生的包 MyModule 将作为单一的逻辑模块:

    >>> import mymodule
    >>> a = mymodule.A()
    >>> a.spam()
    A.spam
    
    >>> b = mymodule.B()
    >>> b.bar()
    B.bar
    

    讨论

    下面两种 import 语句。

    from mymodule.a import A
    from mymodule.b import B
    

    from mymodule import A, B
    

    对后者而言,让 mymodule 成为一个大的源文件更常见。

    第四节展示了如何将多个文件合并成单一的逻辑命名空间。步骤的关键是创建一个包目录,使用 __init__.py 文件将每部分粘合在一起


    作为这一节的延伸,下面介绍延迟导入。之前 __init__.py 文件一次导入所有的组件。

    但是对于一个很大的模块,可能只想组件在需要时被加载。要做到这一点,__init__.py 有细微的变化:

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

    类 A 和类 B 被替换为在第一次访问时加载所需的类的函数。对于用户,没太大不同。

    >>> import mymodule
    >>> a = mymodule.A()
    >>> a.spam()
    A.spam
    >>>
    

    延迟加载的主要缺点是继承和类型检查可能会中断。需要稍微改变你的代码:

    if isinstance(x, mymodule):		# 错误
        ...
    if isinstance(x, mymodule.a.A):	# 正确
        ...
    



    五、利用命名空间导入目录分散的代码

    问题

    当代码由不同的人来维护,你希望用共同的包前缀将所有组件连接起来。


    解决方案

    定义一个顶级 Python 包,作为一个大集合分开维护子包。

    在不同的目录里统一相同的命名空间,但是要删除用来将组件联合起来的 __init__.py 文件。

    foo-package/
    	spam/
        	blah.py
            
    bar-package/
    	spam
        	grok.py
    

    在上面的两个目录,有共同的命名空间 spam。但是都没有 __init__.py 文件。

    foo-packagebar-package 都加到 python 模块路径:

    >>> import sys
    >>> sys.path.extend(['foo-package', 'bar-package'])
    >>> import spam.blah
    >>> import spam.grok
    >>>
    

    两个不同的包目录被合并到一起,你可以导入spam.blahspam.grok ,并能够正常工作。


    讨论

    包命名空间的关键是确保顶级目录中没有 __init__.py 文件来作为共同的命名空间。




  • 相关阅读:
    java常量池概念【转】
    IntelliJ IDEA 2016 汉化说明:
    intellij idea 添加动态 user library(java.lang.VerifyError)【转】
    IntelliJ IDEA 12 创建Web项目 教程 超详细版【转】
    IntelliJ IDEA 12详细开发教程(一)思想的转变与新手入门【转】
    接口请求时,charles修改请求参数
    monkey测试工具
    操作DOM
    操作表单
    jQuery扩展
  • 原文地址:https://www.cnblogs.com/keye/p/15389249.html
Copyright © 2011-2022 走看看