as python里的import python 中import_Python

简介

简单来看,import机制可以导入我们需要使用的库,避免代码重复,使用方便,可谓是编写Python时最常使用写法,但我们了解import吗?

import其实有很多容易混淆的概念以及可以实现很多非常有趣的玩法,本篇文章抛砖引玉,聊聊import

需注意,Python2与Python3的import机制有较大差别,主要体现在两个节点,Python2.6之前使用relative import(相对导入)作为默认import机制,Python2.6之后使用absolute import(绝对导入)作为默认import机制;Python3.3出现命名空间包的概念。

简单而言,Python3.7与Python2.7在import机制上有较大差异,这里以Python3.7为基准进行讨论。

模块、常规包与命名空间包

「模块」、「常规包」与「命名空间包」是理解import机制绕不开的概念。

「模块」:一个以「.py」为后缀的文件就是一个模块 「常规包」:「__init__.py」所在目录就是一个常规包 「命名空间包」:命名空间包是一种虚拟的概念,它由多个子包构成,这些子包可以在任意位置,可以为zip中的文件或网络上的文件,这些子包在概念上是一个整体,这个整体就是一个命名空间包

「常规包」与「命名空间包」的概念在PEP420被提出,在Python3.3及之后的Python版本中实现,此前只有「常规包」这一种包。

import导入系统

通过import机制,我们可以使用导入模块内的代码。

import是最常用的导入机制,但并不是唯一的方式,importlib模块以及内置的__import__()方法都可以实现模块的导入。

import语句做的是两个操作

1.搜索操作:在相应的路径中搜索指定名称的模块 2.绑定操作:将搜索到的结果绑定到当前作用域对应的名称上(即创建module对象,并初始化)

通过阅读Python官方文档可知,import的搜索操作通过__import__()方法实现,而绑定操作只有import语句才能做到。

当某个模块初次被import时,Python会在相应的路径去搜索该模块,如果模块存在,则在当前作用域下创建一个module对象并进行初始化,如果未找到,则抛出 ModuleNotFoundError。

import在导入模块时,会根据sys.path中定义的路径来搜索对应的模块,sys.path是一个list,import时会先从sys.path中下标为0的路径开始搜索,我们可以将需要的路径添加到sys.path。

如果我们没有修改,sys.path中默认的路径为:

  • 1.当前目录的路径
  • 2.环境变量PYTHONPATH中指定的路径列表
  • 3.Python安装路径的lib目录所在路径

要修改搜索路径有3种方式:

  • 1.动态修改sys.path,因为sys.path为list,所以我们可以很轻松的操作list实现搜索路径的修改。这种方式只会对当前项目临时生效
  • 2.修改PYTHONPATH环境变量,这种方式会永久生效,而且所有的Python项目都会受到影响,因为Python程序启动时会自动去读取该环境良好的值。
  • 3.增加.pth后缀的文件。在sys.path已有的某一个目录下添加.pth后缀的配置文件,该文件的内容就是要添加的搜索路径,Python在遍历已有目录的过程中,如果遇到.pth文件,就会将其中的路径添加到sys.path中。

Python中定义了多种搜索策略去搜索相应的模块,而这些策略可以通过importlib等提供的各类hook进行修改实现一些有趣的效果。

在Python3.3之后,所有的模块导入机制都会通过「sys.meta_path」暴露,不会在有任何隐式导入机制。

In [1]: import sys
In [2]: sys.meta_path
Out[2]:
[_frozen_importlib.BuiltinImporter,
 _frozen_importlib.FrozenImporter,
 _frozen_importlib_external.PathFinder,
 <six._SixMetaPathImporter at 0x10f43e860>]

import形式

import主要有两种形式

1.import xxx

2.from xxx import xxx

可以看到下面的例子:

In [1]: import os
In [2]: from os.path import join
In [3]: import os.path.join
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
<ipython-input-3-26fe2f8f50ea> in <module>
----> 1 import os.path.join
ModuleNotFoundError: No module named 'os.path.join'; 'os.path' is not a package

可以看出,第2行与第3行的区别在于是否使用from,其背后的规则是:

  • 1.单独使用import时,import后面只能接模块或包(常规包或命名空间包)
  • 2.使用from xxx import xxx 时,from后只能接模块或包,而此时import后可以接任何变量(模块、包或模块中具体的方法)

在导入模块的过程中,首先会搜索sys.modules中内容,sys.modules是模块的缓存,其中包括了所有此前导入的模块。

  • 1.使用的是 import A, 此时会先检查sys.moduels中是否存在A,如果存在,则不会再去搜索加载,而是直接从缓存中取来用,如果没有,则搜索模块并进行绑定操作,随后便将其加载到sys.modules中
  • 2.使用的是 from A import B,此时依旧会先检查A并进行相同的操作,随后获得A的module对象后,从中解析并寻找B然后再填充到A的__dict__结构中,从而实现不用标明来自于A,直接通过B的名称就可直接使用的B的效果。

相对导入与绝对导入

当我们import导入模块或包时,Python提供两种导入方式:

1.相对导入 relative import 2.绝对导入 absolute import

在Python2.6之前,Python默认使用的相对导入,而Python2.6之后,以绝对导入为默认使用的导入方式。

这两个概念与相对路径、绝对路径的概念有些相似,几个具体的例子:

1.绝对导入格式为:import A.B.C 或者 form A.B import C 2.相对导入格式为:from . import B 或 from ..A import B,其中.表示当前模块,..表示上层模块(与相对路径概念类似)

在开发第三方模块时,通常使用相对导入的形式,这可以有效避免硬编码带来的问题,比如在开发过程中,我们修改了某个文件的名称,此时,使用绝对导入形式的import就需要修改相应的名称。这其实也不是绝对的,当包的结构发生改变时,相对导入就会出现问题,当相对模块名称的改变而言,目录结构的改变出现概率会小一些。

import中的基本概念就简单介绍到这里,这些概念中还有很多细节值得学习,这些细节在后面的章节中慢慢给出。

最后感谢大家阅读