python之import机制详解


Posted in Python onJuly 03, 2014

本文详述了Python的import机制,对于理解Python的运行机制很有帮助!

1.标准import:

Python中所有加载到内存的模块都放在 sys.modules 。当 import 一个模块时首先会在这个列表中查找是否已经加载了此模块,如果加载了则只是将模块的名字加入到正在调用 import 的模块的 Local 名字空间中。如果没有加载则从 sys.path 目录中按照模块名称查找模块文件,模块可以是py、pyc、pyd,找到后将模块载入内存,并加到 sys.modules 中,并将名称导入到当前的 Local 名字空间。

一个模块不会重复载入。多个不同的模块都可以用 import 引入同一个模块到自己的 Local 名字空间,其实背后的 PyModuleObject 对象只有一个。这里说一个容易忽略的问题:import 只能导入模块,不能导入模块中的对象(类、函数、变量等)。例如:模块 A(A.py)中有个函数 getName,另一个模块不能通过 import A.getName 将 getName导入到本模块,只能用 from A import getName。

2.嵌套import:

1)顺序嵌套

例如:本模块导入 A 模块(import A),A 中又 import B,B 模块又可以 import 其他模块……
这中嵌套比较容易理解,需要注意的一点就是各个模块的 Local 名字空间是独立的。对于上面的例子,本模块 import A 之后本模块只能访问模块 A,不能访问模块 B 及其他模块。虽然模块 B 已经加载到内存了,如果访问还要再明确的在本模块中 import B。

2)循环嵌套

例如:

文件[A.py]

from B import D
class C:pass

文件[ B.py ]

from A import C
class D:pass

为什么执行 A 的时候不能加载 D 呢?
如果将 A.py 改为:import B 就可以了。
这是怎么回事呢?

RobertChen:这跟Python内部 import 的机制是有关的,具体到 from B import D,Python 内部会分成几个步骤:
(1)在 sys.modules 中查找符号 “B”
(2)如果符号 B 存在,则获得符号 B 对应的 module 对象。
  从 <modult B> 的 __dict__ 中获得符号 “D” 对应的对象,如果 “D” 不存在,则抛出异常。
(3)如果符号 B 不存在,则创建一个新的 module 对象 <module B>,注意,此时,module 对象的 __dict__ 为空。
执行 B.py 中的表达式,填充 <module B> 的 __dict__。
从  <module B> 的 __dict__ 中获得 “D” 对应的对象,如果 “D” 不存在,则抛出异常。

所以这个例子的执行顺序如下:

1、执行 A.py 中的 from B import D 由于是执行的 python A.py,所以在 sys.modules 中并没有 <module B> 存在, 首先为 B.py 创建一个 module 对象 (<module B>) , 注意,这时创建的这个 module 对象是空的,里边啥也没有, 在 Python 内部创建了这个 module 对象之后,就会解析执行 B.py,其目的是填充 <module B> 这个 __dict__。
2、执行 B.py中的from A import C 在执行B.py的过程中,会碰到这一句, 首先检查sys.modules这个module缓存中是否已经存在<module A>了, 由于这时缓存还没有缓存<module A>, 所以类似的,Python内部会为A.py创建一个module对象(<module A>), 然后,同样地,执行A.py中的语句
3、再次执行A.py中的from B import D 这时,由于在第1步时,创建的<module B>对象已经缓存在了sys.modules中, 所以直接就得到了<module B>, 但是,注意,从整个过程来看,我们知道,这时<module B>还是一个空的对象,里面啥也没有, 所以从这个module中获得符号"D"的操作就会抛出异常。 如果这里只是import B,由于"B"这个符号在sys.modules中已经存在,所以是不会抛出异常的。

ZQ:图解如下:

python之import机制详解

3. 包 import

只要一个文件夹下面有个 __init__.py 文件,那么这个文件夹就可以看做是一个包。包导入的过程和模块的基本一致,只是导入包的时候会执行此包目录下的 __init__.py 而不是模块里面的语句了。另外,如果只是单纯的导入包,而包的 __init__.py 中又没有明确的其他初始化操作,那么此包下面的模块是不会自动导入的。
 例如:
  有下面的包结构:
  PA
  |---- __init__.py
  |---- wave.py
  |---- PB1
        |---- __init__.py
        |---- pb1_m.py
  |---- PB2
        |---- __init__.py
        |---- pb2_m.py
有如下程序:

import sys
import PA.wave              #1
import PA.PB1                #2
import PA.PB1.pb1_m as m1    #3
import PA.PB2.pb2_m          #4
PA.wave.getName()           #5
m1.getName()                #6
PA.PB.pb2_m.getName()       #7

1) 当执行 #1 后,sys.modules 会同时存在 PA、PA.wave 两个模块,此时可以调用 PA.wave 的任何类或函数了。但不能调用 PA.PB1(2) 下的任何模块。当前 Local 中有了 PA 名字。

2) 当执行 #2 后,只是将 PA.PB1 载入内存,sys.modules 中会有 PA、 PA.wave、PA.PB1 三个模块,但是 PA.PB1 下的任何模块都没有自动载入内存,此时如果直接执行 PA.PB1.pb1_m.getName() 则会出错,因为 PA.PB1 中并没有 pb1_m 。当前 Local 中还是只有 PA 名字,并没有 PA.PB1 名 字。

3) 当执行 #3 后,会将 PA.PB1 下的 pb1_m 载入内存,sys.modules 中会有 PA、PA.wave、PA.PB1、PA.PB1.pb1_m 四个模块,此时可以执行 PA.PB1.pb1_m.getName() 了。由于使用了 as,当前 Local中除了 PA 名字,另外添加了 m1 作为 PA.PB1.pb1_m 的别名。

4) 当执行 #4 后,会将 PA.PB2、PA.PB2.pb2_m 载入内存,sys.modules 中会有 PA、PA.wave、PA.PB1、PA.PB1.pb1_m、PA.PB2、PA.PB2.pb2_m 六个模块。当前 Local 中还是只有 PA、m1。
下面的 #5,#6,#7 都是可以正确运行的。

注意的是:如果 PA.PB2.pb2_m 想导入 PA.PB1.pb1_m、PA.wave 是可以直接成功的。最好是采用明确的导入路径,对于 ./.. 相对导入路径还是不推荐用。

Python 相关文章推荐
Python 可爱的大小写
Sep 06 Python
Python中列表元素转为数字的方法分析
Jun 14 Python
Python可变参数用法实例分析
Apr 02 Python
matplotlib绘制符合论文要求的图片实例(必看篇)
Jun 02 Python
Python tkinter事件高级用法实例
Jan 31 Python
浅谈Python中的zip()与*zip()函数详解
Feb 24 Python
浅析python继承与多重继承
Sep 13 Python
选择python进行数据分析的理由和优势
Jun 25 Python
python小程序实现刷票功能详解
Jul 17 Python
Python三元运算与lambda表达式实例解析
Nov 30 Python
解决python3.6用cx_Oracle库连接Oracle的问题
Dec 07 Python
python实现黄金分割法的示例代码
Apr 28 Python
Python之eval()函数危险性浅析
Jul 03 #Python
python的绘图工具matplotlib使用实例
Jul 03 #Python
python绘图库Matplotlib的安装
Jul 03 #Python
Python实现全局变量的两个解决方法
Jul 03 #Python
Python实现端口复用实例代码
Jul 03 #Python
在 Django/Flask 开发服务器上使用 HTTPS
Jul 03 #Python
采用python实现简单QQ单用户机器人的方法
Jul 03 #Python
You might like
php中文字符截取防乱码
2008/03/28 PHP
XAMPP升级PHP版本实现步骤解析
2020/09/04 PHP
js和jquery对dom节点的操作(创建/追加)
2013/04/21 Javascript
JavaScript中的eval()函数详解
2013/08/22 Javascript
Jquery Ajax解析XML数据(同步及异步调用)简单实例
2014/02/12 Javascript
javascript 获取函数形参个数
2014/07/31 Javascript
使用Node.js实现HTTP 206内容分片的教程
2015/06/23 Javascript
图解Sublime Text3使用技巧
2015/12/21 Javascript
获取jqGrid中选择的行的数据
2016/11/30 Javascript
详解jQuery停止动画——stop()方法的使用
2016/12/14 Javascript
jQuery Plupload上传插件的使用
2017/04/19 jQuery
JavaScript实现打地鼠小游戏
2020/04/23 Javascript
详解Vue学习笔记进阶篇之列表过渡及其他
2017/07/17 Javascript
微信小程序录音与播放录音功能
2017/12/25 Javascript
全面介绍vue 全家桶和项目实例
2017/12/27 Javascript
js将键值对字符串转为json字符串的方法
2018/03/30 Javascript
JS实现点击生成UUID的方法完整实例【基于jQuery】
2019/06/12 jQuery
jQuery实现弹幕特效
2019/11/29 jQuery
vue-cli4项目开启eslint保存时自动格式问题
2020/07/13 Javascript
Vue开发中常见的套路和技巧总结
2020/11/24 Vue.js
教你安装python Django(图文)
2013/11/04 Python
python爬虫入门教程--正则表达式完全指南(五)
2017/05/25 Python
python实现百度语音识别api
2018/04/10 Python
pd.DataFrame统计各列数值多少的实例
2019/12/05 Python
浅谈Tensorflow 动态双向RNN的输出问题
2020/01/20 Python
Python+Kepler.gl轻松制作酷炫路径动画的实现示例
2020/06/02 Python
Django中Q查询及Q()对象 F查询及F()对象用法
2020/07/09 Python
如何用H5实现一个触屏版的轮播器的实例
2017/01/09 HTML / CSS
美国小蜜蜂Burt’s Bees德国官网:天然唇部、皮肤和身体护理产品
2020/06/14 全球购物
2014自主招生自荐信策略
2014/01/27 职场文书
高中英语演讲稿范文
2014/04/24 职场文书
做一个有道德的人演讲稿
2014/05/14 职场文书
教师批评与自我批评材料
2014/10/16 职场文书
2015年元旦文艺晚会总结(学院)
2014/11/28 职场文书
2014年移动公司工作总结
2014/12/08 职场文书
浅谈JS和Nodejs中的事件驱动
2021/05/05 NodeJs