python之import机制详解
作者:shichen2014 发布时间:2022-11-29 02:47:40
本文详述了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:图解如下:
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文件操作函数用法实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋
- 目录1、几个概念2、举个例子3、解决问题4、换个方法通常,当我们需要对对象的敏感属性或者不希望外部直接访问的属性进行私有化,但是某些时候我们
- 要自动发微博最简单的办法无非是调用新浪微博的API(因为只是简单的发微博,就没必要用它的SDK了)。参考开发文档http://o
- loc和iloc的意思首先,loc是location的意思,和iloc中i的意思是指integer,所以它只接受整数作为参数,详情见下面。l
- 正三角形九九乘法表#正三角形九九乘法表for i in range(1,10): for j in range(1
- 图像的全景拼接包括三大部分:特征点提取与匹配、图像配准、图像融合。1、基于SIFT的特征点的提取与匹配利用Sift提取图像的局部特征,在尺度
- 你是否苦恼于网上无法下载的“小说在线阅读”内容?或是某些文章的内容让你很有收藏的冲动,却找不到一个下载的链接?是不是有种自己写个程序把全部搞
- 加载垃圾邮件数据集spambase.csv(数据集基本信息:样本数: 4601,特征数量: 57, 类别:1 为垃圾邮件,0 为
- 1.在查询分析器理启动或停止SQL Agent服务 启动 use master go xp_cmdshell
- 1. mysql_where子句_聚合函数# ### part 单表查询""" select ... from
- 可能有些地方翻译得不好,请见谅在这个冠冕堂皇的标题之下,我想回答一个土方,有人在博客提出很久了。该土方很好的描述了很多图标设计师所面临的典型
- 本文实例讲述了Python设计实现的计算器功能。分享给大家供大家参考,具体如下:通过利用PYTHON 设计处理计算器的功能如:1 - 2 *
- 操作系统:WINDOWS-XP 系统数据库版本:mysql 5.x提示:access denied for user 'root
- 本文实例讲述了python根据出生日期返回年龄的方法。分享给大家供大家参考。具体实现方法如下:def CalculateAge(self,
- 本文实例讲述了python复制文件的方法。分享给大家供大家参考。具体分析如下:这里涉及Python复制文件在实际操作方案中的实际应用以及Py
- 在python中调用fortran代码,要用到f2py这个程序。它的项目主页在此。现在该项目已经合并到numpy中了,先安装python再装
- 操作系统:win7 64位旗舰版mysql压缩包:mysql-5.7.11-winx64.zip1. 解压MySQL压缩包将下载的MySQL
- 先上两段代码<script>var i = 2;function test(){var i = 1;}test();alert(
- 1、ALL OR ANYPython 之所以成为这么一门受欢迎的语言一个原因是它的可读性和表达能力非常强。Python 也因此经常被调侃为&
- 文件提交页面既已生成,下面任务就很明确了:将提交的文件内容保存到服务器上。 下面我们用两种方法来实现这个功能: 1. 用 PHP 来保存: