python之导入模块与包
# 我们导入模块有可能重复导入,为了防止你重复导入,python的优化手段是: # 第一次导入后就将模块名加载到内存了, # 后续的import语句仅是对已经加载大内存中的模块对象增加了一次引用, # 不会重新执行模块内的语句。 # 我们可以从sys.module中找到当前已经加载的模块, # sys.module是一个字典,内部包含模块名与模块对象的映射, # 该字典决定了导入模块时是否需要重新导入 # 查看是不是已经导入了,查看内存中是否有模块: import sys print(sys.modules)
import span # 导入时候,发生了什么? # 第一件事:创建名称空间,用来存放span.py中定义的名字, # 第二件事:基于刚刚创建的名称空间执行span.py文件 # 第三件事:创建名字spam指向该名称空间,来引用该命名空间,span.名字对操作,都是以这个为准 print(span.money) print(hasattr(span,"money")) # 反射为True
from ...import ...导入方式:
#要导入的span.py print('from the spam.py') money=1000 def read1(): print('spam->read1->money',1000) def read2(): print('spam->read2 calling read') read1() def change(): global money money=0
from span import money,read1,read2 # 将span中的名字直接导入到当前的名称空间中,在当前名称空间中,直接使用名字就可以了,但是如果这个文件有同名变量或者函数就造成了冲突 print(money) print(read1()) print(read2())
模块的搜索路径:
# 所以总结模块的查找顺序是: # 1,内存中已经加载的模块 # 2,内建模块 # 3,sys.path路径中包含的模块,当前路径 # 某种场景下,其他路径模块导入就不行了,所以啊,我们不能把固定的结构目录放入内存和内建,只有加入sys.path
例如:
import os,sys print(__file__) print(os.path.abspath(__file__)) base_path=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) print(base_path) sys.path.append(base_path)
包:包的本质就是一个包含__init__.py文件的目录
导入包:
# 导入包的过程:就是执行包下面的__init__文件
无论是import形式还是from...import形式,凡是在导入语句中(而不是在使用时)遇到带点的,都要第一时间提高警觉:这是关于包才有的导入语法
# import导入包时候,点左边的都必须是包 # from...import...导入包时候,import后边不能有点 # 例如:在glance包下的api包下引入policy.py模块 # from glance.api import policy # 全部引入: # from glance.api import* # 其实*等价于__all__: from package.dir import hello print(hello.x) # 可以用import导入内置或者第三方模块, # 但是要绝对避免使用import来导入自定义包的子模块, # 应该使用from... import ...的绝对或者相对导入,且包的相对导入只能用from的形式。
绝对导入和相对导入:
案例文件目录:
glance/
├── __init__.py ├── api │ ├── __init__.py │ ├── policy.py │ └── versions.py ├── cmd │ ├── __init__.py │ └── manage.py └── db ├── __init__.py └── models.py
绝对导入:以glance作为起始
相对导入:用.或者..的方式最为起始(只能在一个包中使用,不能用于不同目录内)
在glance/api/version.py #绝对导入 from glance.cmd import manage manage.main() #相对导入 from ..cmd import manage manage.main()
把模块当作脚本来运行:
我们可以通过模块的全局变量__name__来查看模块名: 当做脚本运行: __name__ 等于'__main__' 当做模块导入: __name__= 作用:用来控制.py文件在不同的应用场景下执行不同的逻辑 if __name__ == '__main__':
编译python文件:
# 为了提高模块的加载速度,Python缓存编译的版本。
# 每个模块在__pycache__目录的以module.version.pyc的形式命名,通常包含了python的版本号,
# 如在CPython版本3.3,关于spam.py的编译版本将被缓存成__pycache__/spam.cpython-33.pyc,
# 这种命名约定允许不同的版本,不同版本的Python编写模块共存。
#
# Python检查源文件的修改时间与编译的版本进行对比,如果过期就需要重新编译。这是完全自动的过程。
# 并且编译的模块是平台独立的,所以相同的库可以在不同的架构的系统之间共享,即pyc使一种跨平台的字节码,
# 类似于JAVA.NET,是由python虚拟机来执行的,但是pyc的内容跟python的版本相关,不同的版本编译后的pyc文件不同,
# 2.5编译的pyc文件不能到3.5上执行,并且pyc文件是可以反编译的,因而它的出现仅仅是用来提升模块的加载速度的。
#
# 在速度上从.pyc文件中读指令来执行不会比从.py文件中读指令执行更快,只有在模块被加载时,.pyc文件才是更快的