# `runpy` ——查找并执行 Python 模块

源代码： Lib/runpy.py

`runpy` 模块用于找到并运行 Python 的模块，而无需首先导入。主要用于实现 `-m` 命令行开关，以允许用 Python 模块命名空间而不是文件系统来定位脚本。

请注意，这 并非 一个沙盒模块——所有代码都在当前进程中运行，所有副作用（如其他模块对导入操作进行了缓存）在函数返回后都会留存。

此外，在 `runpy` 函数返回后，任何由已执行代码定义的函数和类都不能保证正确工作。如果某使用场景不能接收此限制，那么选用 `importlib` 可能更合适些。

`runpy` 模块提供两个函数：

`runpy.``run_module`(mod_name, init_globals=None, run_name=None, alter_sys=False)

  执行给定模块的代码，并返回结果模块的 globals 字典。 该模块的代码首先会用标准的导入机制去查找定位 (详情请参阅 PEP 302)，然后在全新的模块命名空间中运行。

  参数 mod_name 应该是一个绝对模块名。如果模块名指向一个包，而不是普通的模块，那么该包会被导入，然后执行包中的 `__main__` 子模块，并返回结果模块的 globals 字典。

  可选的字典参数 init_globals 可用来在代码执行前预填充模块的 globals 字典。给出的字典参数不会被修改。如果字典中定义了以下任意一个特殊全局变量，这些定义都会被 `run_module()` 覆盖。

  The special global variables `__name__`, `__spec__`, `__file__`, `__cached__`, `__loader__` and `__package__` are set in the globals dictionary before the module code is executed (Note that this is a minimal set of variables - other variables may be set implicitly as an interpreter implementation detail).

  若可选参数 `__name__` 不为 `None` 则设为 run_name，若此名称的模块是一个包则设为 `mod_name + '.__main__'`，否则设为 mod_name 参数。

  `__spec__` 将设为合适的 实际 导入模块（也就是说，`__spec__.name` 一定是 mod_name 或 `mod_name + '.__main__`，而不是 run_name）。

  `__file__` 、`__cached__`、 `__loader__` 和 `__package__` 根据模块规范进行 正常设置

  如果给出了参数 alter_sys 并且值为 `True`，那么 `sys.argv[0]` 将被更新为 `__file__` 的值，`sys.modules[__name__]` 将被更新为临时模块对象。在函数返回前， `sys.argv[0]` 和 `sys.modules[__name__]` 将会复原。

  注意，这种对 `sys` 的操作不是线程安全的。其他线程可能会看到初始化不完整的模块，以及变动后的参数列表。如果从线程中的代码调用此函数，建议单实例运行 `sys` 模块。

  也參考

  `-m` 选项由命令行提供相同功能。

  3.1 版更變: 加入了 查找 `__main__` 子模块并执行软件包的能力。

  3.2 版更變: 加入了 `__cached__` 全局变量（参见 PEP 3147 ）。

  3.4 版更變: 充分利用 PEP 451 加入的模块规格功能。使得以这种方式运行的模块能够正确设置 `__cached__`，并确保真正的模块名称总是可以通过 `__spec__.name` 的形式访问。

`runpy.``run_path`(file_path, init_globals=None, run_name=None)

  执行指定位置的代码，并返回结果模块的 globals 字典。与提供给 CPython 命令行的脚本名称一样，给出的路径可以指向 Python 源文件、编译过的字节码文件或包含``__main__`` 模块的有效 sys.path 项（例如一个包含顶级``__main__.py`` 文件的 zip 文件）。

  对于直接的脚本而言，指定代码将直接在一个新的模块命名空间中运行。对于一个有效的 sys.path 项（通常是一个 zip 文件或目录），其首先会被添加到 `sys.path` 的开头。然后，本函数用更新后的路径查找并执行 `__main__` 模块。请注意，即便在指定位置不存在主模块，也没有特别的保护措施来防止调用存在于 `sys.path` 其他地方的 `__main__`。

  利用可选的字典参数 init_globals ，可在代码执行前预填模块的 globals 字典。给出的字典参数不会被修改。如果给出的字典中定义了下列特殊全局变量，这些定义均会被 `run_module()` 覆盖。

  The special global variables `__name__`, `__spec__`, `__file__`, `__cached__`, `__loader__` and `__package__` are set in the globals dictionary before the module code is executed (Note that this is a minimal set of variables - other variables may be set implicitly as an interpreter implementation detail).

  如果该可选参数不为 `None`，则 `__name__` 被设为 run_name，否则为 `'<run_path>'`。

  如果提供的路径直接引用了一个脚本文件（无论是源码文件还是预编译的字节码），那么 `__file__` 将设为给出的路径，而 `__spec__`、`__cached__`、`__loader__` 和 `__package__` 都将设为 `None`。

  如果给出的路径是对有效 sys.path 项的引用，那么 `__spec__` 将为导入的 `__main__` 模块进行正确设置 (也就是说，`__spec__.name` 将一定是 `__main__`)。 `__file__`, `__cached__`, `__loader__` 和 `__package__` 将依据模块规格 进行常规设置。

  `sys` 模块也做了一些改动。 首先，如上所述，`sys.path` 可能会被修改。 `sys.argv[0]` 被更新为 `file_path` 的值，`sys.modules[__name__]` 被更新为正在被执行的模块的临时模块对象。 在函数返回之前，对 `sys` 中条目的所有修改都会被复原。

  注意，与 `run_module()` 不同的是，本函数对 `sys` 的修改不是可有可无的，因为这些调整对于 sys.path 项能够顺利执行至关重要。由于依然存在线程安全的限制，在线程代码中使用本函数时应采用导入锁进行序列运行，或者委托给一个单独的进程。

  也參考

  接口选项 用于在命令行上实现同等功能（`python path/to/script`）。

  3.2 版新加入.

  3.4 版更變: 已作更新，以便充分利用 PEP 451 加入的模块规格功能。使得从有效 sys.path 项导入``__main__`` 而不是直接执行的情况下，能够正确设置 `__cached__`。

也參考

PEP 338 -- 将模块作为脚本执行

  PEP 由 Nick Coghlan 撰写并实现。

PEP 366 ——主模块的显式相对导入

  PEP 由 Nick Coghlan 撰写并实现。

PEP 451 —— 导入系统采用的 ModuleSpec 类型

  PEP 由 Eric Snow 撰写并实现。

命令行与环境 —— CPython 命令行详解

`importlib.import_module()` 函数