前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python import 原理剖析

python import 原理剖析

原创
作者头像
zero000
修改2021-07-14 18:06:12
2.5K0
修改2021-07-14 18:06:12
举报
文章被收录于专栏:程序员菜谱程序员菜谱

一、简介

Python code in one module gains access to the code in another module by the process of importing it.

简单来说,我们日常看到的.py文件,都称作是一个module

当你的 python 代码需要获取外部的一些功能(一些已经造好的轮子),你就需要使用到 import 这个声明关键字。import可以协助导入其他 module 。(类似 C 预约的 include

import 声明是常见的导入方式,但它不是唯一的方式。即其实可以通过其他方式进行 module 导入。

import 语句结合了两个操作:

  1. 搜索命名模块。通过传入合适的参数调用 __import()__ 实现。
  2. 将搜索的结果绑定到本地命名空间__import()__ 的返回值用作命名空间绑定操作。

import 语句执行时,__import__() 会被调用,Python 会查找 module 并创建一个 module object 并初始化它;如果 module 没找到,会抛出 ModuleNotFoundError的一个 exception 。

import vs __import__()

简单来说,调用__import__() 只是 import 声明操作的一个子集。

直接调用 import() 仅执行模块搜索,如果找到,则执行模块创建操作。虽然可能会出现某些副作用,例如导入父包,以及更新各种缓存(包括 sys.modules),但只有 import 语句执行名称绑定操作。

二、查找 Module 的方式

When a module named spam is imported, the interpreter first searches for a built-in module with that name. If not found, it then searches for a file named spam.py in a list of directories given by the variable sys.path. sys.path is initialized from these locations:The directory containing the input script (or the current directory when no file is specified). PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH). The installation-dependent default.

import 执行时,会尝试使用以下顺序查找 module:

  1. 解析器首先尝试搜索自身内置的 module
  2. 如果找不到,就会根据 sys.path 的顺序查找
    1. py 执行文件本身所在文件夹;
    2. PYTHONPATH 环境变量;
    3. python 默认的安装依赖位置

可以通过下面操作查看sys.path的路径

代码语言:txt
复制
$ python3
Python 3.5.2 (default, Jan 26 2021, 13:30:48) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', '/home/tester/opt/2.7.5.1', '/usr/lib/python35.zip', '/usr/lib/python3.5', '/usr/lib/python3.5/plat-x86_64-linux-gnu', '/usr/lib/python3.5/lib-dynload', '/usr/local/lib/python3.5/dist-packages', '/usr/lib/python3/dist-packages']

所以,如果需要使用一些第三方的库,除了通过 pip 安装后直接调用以外,也可以把module放置到对应的目录,然后使用 PYTHONPATH 指定该目录。

三、import 导入深入探究

  • 导入需要module完整的路径,Python 会尝试自上而下导入。当 python 尝试导入 foo.bar.baz 时,会先尝试导入 foo ,然后foo.bar,最后foo.bar.baz,如果任何一个中间导入失败,会触发ModuleNotFoundError
  • 导入有cache的概念。而每次导入首先会尝试到 sys.modules 这个 cache进行查找,如果返回 None 则会抛出 ModuleNotFoundError 错误,如果 module name 找不到,Python 会尝试继续往下查找
  • import 的最新底层机制,是通过 finders and loaders 两者结合查找 module 并进行导入操作,finders 负责查找相关路径, loaders 负责加载。

实际 module 查找顺序

Python’s default sys.meta_path has three meta path finders, one that knows how to import built-in modules, one that knows how to import frozen modules, and one that knows how to import modules from an import path (i.e. the path based finder).

有上文可知,python import 时候会遵循一定的查找顺序。除了第二章说的3个路径,实际前置还有一层 cache

  1. sys.modules,The module cache.
  2. sys.meta_path: 一般有3个finders
    1. one that knows how to import built-in modules
    2. one that knows how to import frozen modules
    3. one that knows how to import modules from an import path

这3个finders对应的路径,恰好是上面第二章说的3个搜索路径

可以通过 Python 交互命令进行查看

代码语言:txt
复制
guotianwei@office-desktop[SJC]~$ python3
Python 3.5.2 (default, Nov 23 2017, 16:37:01) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> print(sys.meta_path)
[<class '_frozen_importlib.BuiltinImporter'>, <class '_frozen_importlib.FrozenImporter'>, <class '_frozen_importlib_external.PathFinder'>]

四、相对导入和绝对导入

相对导入,以 . 打头,跟 linux 文件系统类似,... 分别代表当前 package 和父级 package

绝对导入,一般是 import <> or from <> import <> 这种形式

例如:

package 目录结构如下

代码语言:txt
复制
package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
        moduleY.py
    subpackage2/
        __init__.py
        moduleZ.py
    moduleA.py

下面的都是相对导入

代码语言:txt
复制
from .moduleY import spam
from .moduleY import spam as ham
from . import moduleY
from ..subpackage1 import moduleY
from ..subpackage2.moduleZ import eggs
from ..moduleA import foo

下面是绝对导入

代码语言:txt
复制
import package.subpackage1.moduleX
from package.subpackage1 import moduleX

五、引申

一般我们使用 import 导入 module 时,应该遵循怎样的原则,PEP8 给了如下建议:

Imports should be grouped in the following order:Standard library imports. Related third party imports. Local application/library specific imports. You should put a blank line between each group of imports.

import 组织顺序:

  1. 标准库 import
  2. 第三方库 import
  3. 本地应用或者库的 import

importlib

Python 新版本提供了一个 api可以控制 import 的规则,避免使用老的方式直接变更 __import__() 复杂的操作,并减少错误发生的概念。

importlib 模块提供了丰富的 API 来与导入系统进行交互。例如 importlib.import_module() 提供了一个推荐的、比内置的 __import__() 更简单的 API 来调用导入机制。

Searching

sys.modules 会在 module import 完成后更新 cache,供下次 import 快速访问。

sys.modules, The module cache This mapping serves as a cache of all modules that have been previously imported, including the intermediate paths. So if foo.bar.baz was previously imported, sys.modules will contain entries for foo, foo.bar, and foo.bar.baz. Each key will have as its value the corresponding module object.

finder 的工作是搜索,跟 loader 工作分离

A finder’s job is to determine whether it can find the named module using whatever strategy it knows about.

搜索路径,不只是 sys.path 的所有路径,一些 subpackages 的查找可能依赖于 parent package 的 __path__

import path: A list of locations (or path entries) that are searched by the path based finder for modules to import. During import, this list of locations usually comes from sys.path, but for subpackages it may also come from the parent package’s __path__ attribute.

import 机制是可扩展的,详细查看 Import hooks 这个概念。有两个主要的 import hooks: meta hooksimport path hooks

The import machinery is designed to be extensible; the primary mechanism for this are the import hooks. There are two types of import hooks: meta hooks and import path hooks.

finders

3个默认的finder,对应不同的策略查找 module

Python’s default sys.meta_path has three meta path finders, one that knows how to import built-in modules, one that knows how to import frozen modules, and one that knows how to import modules from an import path (i.e. the path based finder).

参考

  1. https://docs.python.org/3/reference/import.html
  2. https://docs.python.org/3/tutorial/modules.html#the-module-search-path
  3. https://www.mediumcn.com/python3/what-happens-behind-the-scenes-when-we-import-a-module-in-python
  4. http://sinhub.cn/2019/05/python-import-machinery-part-two/
  5. https://stackoverflow.com/questions/9586630/python-paths-and-import-order

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、简介
    • import vs __import__()
    • 二、查找 Module 的方式
    • 三、import 导入深入探究
      • 实际 module 查找顺序
      • 四、相对导入和绝对导入
      • 五、引申
        • importlib
          • Searching
            • finders
            • 参考
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档