美文网首页
Python教程第五章 模块

Python教程第五章 模块

作者: 方雲 | 来源:发表于2020-11-12 17:21 被阅读0次

    本系列文章是我学习Python3.9的官方tutorial的笔记,大部分来源于官网的中文翻译,但由于该翻译有些部分实在太差和啰嗦,我做了很多删除和修改,还有部分原文讲不明白的,我参考其他资料增加了进一步阐述说明。

    模块是一个包含Python定义和语句的文件。文件名就是模块名后跟文件后缀 .py 。在一个模块内部,模块名(作为一个字符串)可以通过全局变量 name 的值获得。例如,使用你最喜爱的文本编辑器在当前目录下创建一个名为 fibo.py 的文件, 文件中含有以下内容:

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

    现在进入Python解释器,并用以下命令导入该模块:

    >>> import fibo
    

    你可以用模块名访问这些函数:

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

    如果你想经常使用某个函数,你可以把它赋值给一个局部变量:

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

    5.1 有关模块的更多信息


    模块可以包含可执行的语句以及函数定义。这些语句用于初始化模块。它们仅在模块 第一次 在 import 语句中被导入时才执行。
    模块可以导入其它模块。习惯上但不要求把所有 import 语句放在模块(或脚本)的开头。被导入的模块名存放在调入模块的全局符号表中。
    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 *
    >>> fib(500)
    0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
    

    这会调入所有非以下划线(_)开头的名称。 在多数情况下,Python程序员都不会使用这个功能,因为它在解释器中引入了一组未知的名称,而它们很可能会覆盖一些你已经定义过的东西。
    如果模块名称之后带有 as,则跟在 as 之后的名称将直接绑定到所导入的模块。

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

    这种方式也可以在用到 from 的时候使用,并会有类似的效果:

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

    5.1.1. 以脚本的方式执行模块

    当你用下面方式运行一个Python模块:

    python fibo.py <arguments>
    

    模块里的代码会被执行,就好像你导入了模块一样,但是 name 被赋值为 "main"。 这意味着通过在你的模块末尾添加这些代码:

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

    你既可以把这个文件当作脚本又可当作一个可调入的模块来使用, 因为那段解析命令行的代码只有在当模块是以“main”文件的方式执行的时候才会运行:

    $ python fibo.py 50
    0 1 1 2 3 5 8 13 21 34
    

    如果模块是被导入的,那些代码是不运行的。

    5.1.2. 模块搜索路径

    当一个名为 spam 的模块被导入的时候,解释器首先寻找具有该名称的内置模块。如果没有找到,解释器会从 sys.path 变量给出的目录列表里寻找名为 spam.py 的文件。sys.path 初始有这些目录地址:

    • 包含输入脚本的目录(或者未指定文件时的当前目录)。
    • PYTHONPATH (一个包含目录名称的列表,它和shell变量 PATH 有一样的语法)。
    • 取决于安装的默认设置

    5.1.3 编译过的python文件

    为了加速模块载入,Python在_pycache_ 目录里缓存了每个模块的编译后版本,名称为 module.version.pyc ,其中名称中的版本字段对编译文件的格式进行编码; 它一般使用Python版本号。例如,在CPython版本3.3中,spam.py的编译版本将被缓存为 pycache/spam.cpython-33.pyc。此命名约定允许来自不同发行版和不同版本的Python的已编译模块共存。
    Python根据编译版本检查源的修改日期,以查看它是否已过期并需要重新编译。这是一个完全自动化的过程。此外,编译的模块与平台无关,因此可以在具有不同体系结构的系统之间共享相同的库。
    Python在两种情况下不会检查缓存。首先,对于从命令行直接载入的模块,它从来都是重新编译并且不存储编译结果;其次,如果没有源模块,它不会检查缓存。为了支持无源文件(仅编译)发行版本, 编译模块必须是在源目录下,并且绝对不能有源模块。

    给专业人士的一些小建议:

    • 你可以在Python命令中使用 -O 或者 -OO 开关, 以减小编译后模块的大小。 -O 开关去除断言语句,-OO 开关同时去除断言语句和 doc 字符串。由于有些程序可能依赖于这些,你应当只在清楚自己在做什么时才使用这个选项。“优化过的”模块有一个 opt- 标签并且通常小些。将来的发行版本或许会更改优化的效果。
    • 一个从 .pyc 文件读出的程序并不会比它从 .py 读出时运行的更快,.pyc 文件唯一快的地方在于载入速度。
    • compileall 模块可以为一个目录下的所有模块创建.pyc文件。
    • 关于这个过程,PEP 3147 中有更多细节,包括一个决策流程图。

    5.2 标准模块


    Python附带了一个标准模块库。一些模块内置于解释器中;它们提供对不属于语言核心的一些内置操作的访问,以提高效率和提供对系统调用的访问。这些模块的集合是一个配置选项,它也取决于底层平台。例如,winreg 模块只在Windows操作系统上提供。一个特别值得注意的模块 sys,它被内嵌到每一个Python解释器中。变量 sys.ps1sys.ps2 定义用作主要和辅助提示的字符串:

    >>> import sys
    >>> sys.ps1
    '>>> '
    >>> sys.ps2
    '... '
    >>> sys.ps1 = 'C> '
    C> print('Yuck!')
    Yuck!
    C>
    

    这两个变量只有在编译器是交互模式下才被定义。
    sys.path 变量是一个字符串列表,用于确定解释器的模块搜索路径。该变量被初始化为从环境变量 PYTHONPATH 获取的默认路径,或者如果 PYTHONPATH 未设置,则从内置默认路径初始化。你可以使用标准列表操作对其进行修改:

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

    5.3 dir()函数


    内置函数 dir() 可以列出某个对象定义的所有属性。 它返回一个排序过的字符串列表:

    >>> import fibo, sys
    >>> dir(fibo)
    ['__name__', 'fib', 'fib2']
    >>> dir(sys)  
    ['__breakpointhook__', '__displayhook__', '__doc__', '__excepthook__',
     '__interactivehook__', '__loader__', '__name__', '__package__', '__spec__',
     '__stderr__', '__stdin__', '__stdout__', '__unraisablehook__',
     '_clear_type_cache', '_current_frames', '_debugmallocstats', '_framework',
     '_getframe', '_git', '_home', '_xoptions', 'abiflags', 'addaudithook',
     'api_version', 'argv', 'audit', 'base_exec_prefix', 'base_prefix',
     'breakpointhook', 'builtin_module_names', 'byteorder', 'call_tracing',
     'callstats', 'copyright', 'displayhook', 'dont_write_bytecode', 'exc_info',
     'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info',
     'float_repr_style', 'get_asyncgen_hooks', 'get_coroutine_origin_tracking_depth',
     'getallocatedblocks', 'getdefaultencoding', 'getdlopenflags',
     'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile',
     'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval',
     'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info',
     'intern', 'is_finalizing', 'last_traceback', 'last_type', 'last_value',
     'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks',
     'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'pycache_prefix',
     'set_asyncgen_hooks', 'set_coroutine_origin_tracking_depth', 'setdlopenflags',
     'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr',
     'stdin', 'stdout', 'thread_info', 'unraisablehook', 'version', 'version_info',
     'warnoptions']
    

    如果没有参数,dir() 会列出你当前定义的名称:

    >>> a = [1, 2, 3, 4, 5]
    >>> import fibo
    >>> fib = fibo.fib
    >>> dir()
    ['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys']
    

    注意:它会列出所有类型的名称:包括变量,模块,函数,等等。
    dir() 不会列出内置函数和变量的名称。如果你想要这些,它们的定义是在标准模块 builtins 中:

    >>> import builtins
    >>> dir(builtins)  
    ['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException',
     'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning',
     'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError',
     'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning',
     'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False',
     'FileExistsError', 'FileNotFoundError', 'FloatingPointError',
     'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError',
     'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError',
     'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError',
     'MemoryError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented',
     'NotImplementedError', 'OSError', 'OverflowError',
     'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError',
     'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning',
     'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError',
     'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError',
     'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError',
     'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning',
     'ValueError', 'Warning', 'ZeroDivisionError', '_', '__build_class__',
     '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs',
     'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable',
     'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits',
     'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit',
     'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr',
     'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass',
     'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview',
     'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property',
     'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice',
     'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars',
     'zip']
    

    5.4 包

    包是一种通过用“带点号的模块名”来构造 Python 模块命名空间的方法。 例如,模块名 A.B 表示 A 包中名为 B 的子模块。
    这是你的包的可能结构(以分层文件系统的形式表示):

    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 将包含该文件的目录当作包。 在最简单的情况下,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 ,并使其在没有包前缀的情况下可用,因此可以按如下方式使用:

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

    请注意,当使用 from package import item 时,item可以是包的子模块(或子包),也可以是包中定义的其他名称,如函数,类或变量。 import 语句首先测试是否在包中定义了item;如果没有,它假定它是一个模块并尝试加载它。如果找不到它,则引发 ImportError 异常。

    相反,当使用 import item.subitem.subsubitem 这样的语法时,除了最后一项之外的每一项都必须是一个包;最后一项可以是模块或包,但不能是类或函数或变量。

    5.4.1 子包相互引用

    当包被构造成子包时(与示例中的 sound 包一样),你可以使用绝对导入来引用兄弟包的子模块。例如,如果模块 sound.filters.vocoder 需要使用 sound.effects 包中的 echo 模块,它可以使用 from sound.effects import echo 。

    你还可以相对路径的形式来导入:

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

    请注意,相对导入是基于当前模块的名称进行导入的。由于主模块的名称总是 "main" ,因此用作Python应用程序主模块的模块必须始终使用绝对导入。

    相关文章

      网友评论

          本文标题:Python教程第五章 模块

          本文链接:https://www.haomeiwen.com/subject/aelpbktx.html