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 19 Python
Tensorflow卷积神经网络实例进阶
May 24 Python
Python3.6简单反射操作示例
Jun 14 Python
Python实现爬虫抓取与读写、追加到excel文件操作示例
Jun 27 Python
完美解决Python matplotlib绘图时汉字显示不正常的问题
Jan 29 Python
python+selenium实现自动化百度搜索关键词
Jun 03 Python
Python中的上下文管理器相关知识详解
Sep 19 Python
在Python中等距取出一个数组其中n个数的实现方式
Nov 27 Python
Python遍历字典方式就实例详解
Dec 28 Python
Python Selenium参数配置方法解析
Jan 19 Python
浅谈tensorflow中Dataset图片的批量读取及维度的操作详解
Jan 20 Python
python datetime处理时间小结
Apr 16 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定时计划任务与fsockopen持续进程实例
2014/05/23 PHP
php获取url参数方法总结
2014/11/13 PHP
JS刷新框架外页面七种实现代码
2013/02/18 Javascript
jquery配合css简单实现返回顶部效果
2013/09/30 Javascript
两种不同的方法实现js对checkbox进行全选和反选
2014/05/13 Javascript
js数组中如何随机取出一个值
2014/06/13 Javascript
JavaScript setTimeout使用闭包功能实现定时打印数值
2015/12/18 Javascript
详解微信小程序开发—你期待的分享功能来了,微信小程序序新增5大功能
2016/12/23 Javascript
jQuery 防止相同的事件快速重复触发方法
2018/02/08 jQuery
Vue路由history模式解决404问题的几种方法
2018/09/29 Javascript
用vscode开发vue应用的方法步骤
2019/05/06 Javascript
JavaScript中0、空字符串、'0'是true还是false的知识点分享
2019/09/16 Javascript
详解vue修改elementUI的分页组件视图没更新问题
2020/11/13 Javascript
解决vue页面刷新,数据丢失的问题
2020/11/24 Vue.js
详解vue之自行实现派发与广播(dispatch与broadcast)
2021/01/19 Vue.js
[07:48]DOTA2上海特级锦标赛主赛事首日RECAP
2016/03/04 DOTA
python使用Berkeley DB数据库实例
2014/09/26 Python
Python中使用gzip模块压缩文件的简单教程
2015/04/08 Python
批量将ppt转换为pdf的Python代码 只要27行!
2018/02/26 Python
Python使用jsonpath-rw模块处理Json对象操作示例
2018/07/31 Python
详解Python3定时器任务代码
2019/09/23 Python
详解HTML5中rel属性的prefetch预加载功能使用
2016/05/06 HTML / CSS
Ray-Ban雷朋美国官网:全球领先的太阳眼镜品牌
2016/07/20 全球购物
澳大利亚先进的皮肤和激光诊所购物网站:Soho Skincare
2018/10/15 全球购物
维也纳通行证:Vienna PASS
2019/07/18 全球购物
Luxplus荷兰:以会员价购买美容产品等,独家优惠
2019/08/30 全球购物
CAT鞋加拿大官网:CAT Footwear加拿大
2020/08/05 全球购物
.NET笔试题(20个问题)
2016/02/02 面试题
如何理解transaction事务的概念
2015/05/27 面试题
入党自荐书范文
2014/03/09 职场文书
秋季开学典礼主持词
2014/03/19 职场文书
入党综合考察材料
2014/06/02 职场文书
学雷锋倡议书
2015/01/19 职场文书
公司催款律师函
2015/05/27 职场文书
祝福语集锦:给百岁老人祝寿贺词
2019/11/19 职场文书
Android中View.post和Handler.post的关系
2022/06/05 Java/Android