# 包和模块

Python 所有模块均属于唯一的模块对象类型,无论由 Python、C 或其他语言实现。

#

为构建模块化的代码组织体系,Python 通过包实现层次化命名空间管理,形成结构化模块资源池。所有包都是模块,但并非所有模块都是包。或者换句话说,包只是一种特殊的模块。

可以把包看成是文件系统中的目录,并把模块看成是目录中的文件。包既可包含常规模块,又可嵌套包含子包,形成树状结构化的代码容器体系。

常规包 (opens new window)通常以一个包含 __init__.py 文件的目录形式实现,当一个常规包被导入时,这个 __init__.py 文件会隐式地被执行,它所定义的对象会被绑定到该包命名空间中的名称。

__init__.py 文件可以包含与任何其他模块中所包含的 Python 代码相似的代码,Python 将在模块被导入时为其添加额外的属性。

以下文件系统布局定义了一个最高层级的 parent 包和三个子包。

parent/
    __init__.py
    one/
        __init__.py
    two/
        __init__.py
    three/
        __init__.py

导入 parent.one 将隐式地执行 parent/__init__.pyparent/one/__init__.py,后续导入 parent.twoparent.three 则将分别执行 parent/two/__init__.pyparent/three/__init__.py

# 模块导入

一个模块内的 Python 代码通过导入操作就能够访问另一个模块内的代码。

发起调用导入机制 (opens new window)的方式:

# import

import (opens new window) 语句结合了两个操作:

  • 搜索指定名称的模块
  • 将搜索结果绑定到当前作用域中的名称

import (opens new window) 语句的搜索操作被定义为对 __import__() (opens new window) 函数的调用并带有适当的参数,__import__() (opens new window) 的返回值会被用于执行 import 语句的名称绑定操作。

__import__() (opens new window) 的直接调用将仅执行模块搜索,如果找到,则执行模块创建操作。只有 import (opens new window) 语句会执行名称绑定操作。

如果标识符列表改为一个星号 *,则在模块中定义的全部公有名称都将按 import 语句所在的作用域被绑定到局部命名空间。

from module import *
  • 公有名称:在模块中可以被外部直接访问和使用的名称(如函数、类、变量等)。
  • 私有名称:通常以下划线 _ 开头,表示这些名称是模块的内部实现细节,不建议外部访问。

一个模块所定义的公有名称,是由在模块的命名空间中检测一个名为 __all__ 的变量来确定的;如果有定义,它必须是一个字符串列表,其中的项为该模块所定义或导入的名称。

当使用 from module import * 语句时,只有 __all__ 列表中的名称会被导入。

如果没有定义 __all__,Python 会默认将所有不以下划线 _ 开头的名称视为公有名称。

from . import views

__all__ = ["views"]

# _import_

__import__ (opens new window) 函数会由 import (opens new window) 语句发起调用。不建议直接使用 __import__() (opens new window) 而应该用 importlib.import_module() (opens new window)

__import__(name, globals=None, locals=None, fromlist=(), level=0)

本函数会导入模块 namefromlist 给出了应从 name 模块中导入的对象或子模块的名称。level 指定是使用绝对还是相对导入,0 意味着仅执行绝对导入。

name 变量的形式为 package.module 时,通常将会返回最高层级的包,而不是以 name 命名的模块。但是,当给出了非空的 fromlist 参数时,则将返回以 name 命名的模块。

# import time
time = __import__('time', globals(), locals(), [], 0)

# import lxml.html
lxml = __import__('lxml.html', globals(), locals(), [], 0)  # 返回顶层模块

# from lxml.html import fromstring, document_fromstring as doc_fromstring
_temp = __import__('lxml.html', globals(), locals(), ['fromstring', 'document_fromstring'], 0)  # 返回 name 模块
fromstring = _temp.fromstring
doc_fromstring = _temp.document_fromstring

# importlib

importlib (opens new window) 包的主要目的是在 Python 源代码中提供 import (opens new window) 语句的实现。

  • importlib.import_module (opens new window)(name, package=None)

    导入一个模块。参数 name 指定了以绝对或相对导入方式导入什么模块。如果参数 name 使用相对导入的方式来指定,那么 package 参数必须设置为模块包名。