模块与包
模块是个啥
模块是一系列功能的集合体,分为三大类:
- 内置模块
- 第三方模块
- 自定义的模块
一个Python文件本身就是一个模块,文件名m.py,模块名叫m
__init__.py 文件,称为包
为何有模块
- 内置模块与第三方拿来就用,无需定义,可以提升开发效率
- 自定义模块,可以将程序的各部分功能提取出来放到一个模块中,供大家共享,好处是减少代码冗余,程序组织结构清晰
1.1、首次导入模块发生的三件事
- 1.执行xx.py
- 2.产生xx.py的名称空间,将xx.py运行过程中产生的名字都丢带xx的名称空间中
- 3.在当前文件中产生的有一个名字xx,该名字指向2中产生的名称空间
首次导入完后,之后再导入都是直接引用首次导入产生的xx.py的名称空间,不会重复执行代码
1.2、import导入模块使用
"""
1. 可以使用逗号分隔符在一行导入多个模块
import time,sys
2. 导入模块的规范(导入的顺序)
1.python内置的模块
2.第三方模块
3.自定义模块
3. 将导入的模块起个别名(当模块名比较长的时候用as起别名)
import time as t
4. 自定义模块命名应该采用纯小写+下划线风格
5. 可以在函数内导入模块,对比在文件开头导入模块属于全局作用域,在函数内导入的模块则属于局部的作用域。
"""
一个python文件的两种用途
一个Python文件有两种用途,一种被当主程序/脚本执行,另一种被当模块导入,为了区别同一个文件的不同用途,每个py文件都内置了__name__ 变量,该变量在py文件被当做脚本执行时赋值为“main”,在py文件被当做模块导入时赋值为模块名
print(__name__)
if __name__ == '__main__':
print("文件被执行")
else:
print("文件被导入")
1.3、from …import 导入
"""
import导入模块在使用时必须加前缀"模块."
优点:肯定不会与当前名称空间中的名字冲突
缺点:加前缀显得麻烦
from ...import ...导入也发生了三件事
1.产生一个模块的名称空间
2.运行xx.py将运行过程中产生的名字都丢到模块的名称空间去
3. 在当前名称空间拿到一个名字,该名字与模块名称空间中的某一个内存地址
优点:代码更精简
缺点:容易与当前名称空间混淆
一行可以导入多个名字
from time import time,sleep
*:导入模块中的所有名字
from os import *
"""
如果我们需要引用模块中的名字过多的话,可以采用上述的导入形式来达到节省代码量的效果,但是需要强调的一点是:只能在模块最顶层使用的方式导入,在函数内则非法,并且的方式会带来一种副作用,即我们无法搞清楚究竟从源文件中导入了哪些名字到当前位置,这极有可能与当前位置的名字产生冲突。模块的编写者可以在自己的文件中定义__all__ 变量用来控制*代表的意思
__all__=['x','get']
x=1
def get():
print(x)
def change():
global x
x=0
class Foo:
def func(self):
print('from the func')
这样我们在另外一个文件中使用*导入时,就只能导入__all__ 定义的名字了
from foo import *
x
get()
change()
Foo()
1.4、循环导入问题
1.5、模块搜索优先级
模块其实分为四个通用类别,分别是:
1、使用纯Python代码编写的py文件
2、包含一系列模块的包
3、使用C编写并链接到Python解释器中的内置模块
4、使用C或C++编译的扩展模块
优先级
在导入一个模块时,如果该模块已加载到内存中,则直接引用,否则会优先查找内置模块,然后按照从左到右的顺序依次检索sys.path中定义的路径,直到找模块对应的文件为止,否则抛出异常。
sys.path也被称为模块的搜索路径,它是一个列表类型
import sys
print(sys.path)
"""结果如下"""
['E:\\Python\\python\\demo\\day04', 'E:\\Python\\python\\demo', 'D:\\Catalog\\JetBrains\\PyCharm 2022.2\\plugins\\python\\helpers\\pycharm_display', 'C:\\Users\\31812\\AppData\\Local\\Programs\\Python\\Python310\\python310.zip', 'C:\\Users\\31812\\AppData\\Local\\Programs\\Python\\Python310\\DLLs', 'C:\\Users\\31812\\AppData\\Local\\Programs\\Python\\Python310\\lib', 'C:\\Users\\31812\\AppData\\Local\\Programs\\Python\\Python310', 'C:\\Users\\31812\\AppData\\Local\\Programs\\Python\\Python310\\lib\\site-packages', 'D:\\Catalog\\JetBrains\\PyCharm 2022.2\\plugins\\python\\helpers\\pycharm_matplotlib_backend']
列表中的每个元素其实都可以当作一个目录来看:在列表中会发现有.zip或.egg结尾的文件,二者是不同形式的压缩文件,事实上Python确实支持从一个压缩文件中导入模块,我们也只需要把它们都当成目录去看即可。
…
sys.path中的第一个路径通常为空,代表执行文件所在的路径,所以在被导入模块与执行文件在同一目录下时肯定是可以正常导入的,而针对被导入的模块与执行文件在不同路径下的情况,为了确保模块对应的源文件仍可以被找到,需要将源文件foo.py所在的路径添加到sys.path中,假设foo.py所在的路为/pythoner/projects/
import sys
sys.path.append(r'/pythoner/projects/')
import foo
sys.modules查看已经加载带内存中的模块
print('demo1' in sys.modules)
1.6、代码规范
我们在编写py文件时,需要时刻提醒自己,该文件既是给自己用的,也有可能会被其他人使用,因而代码的可读性与易维护性显得十分重要,为此我们在编写一个模块时最好按照统一的规范去编写,如下
"The module is used to..."
import sys
x=1
class Foo:
'Class Foo is used to...'
pass
def test():
'Function test is used to…'
pass
if __name__ == '__main__':
test()
1.7、函数的提示类型
def register(name:str,age:int,hobby:tuple)->int:
print(name)
print(age)
print(hobby)
return 111
register('lisi',22,('JayChou',))
1.8、包
- 包就是一个包含
__init__.py 文件的文件夹 - 包的本质就是模块的一种形式,是用来被当作模块导入
导入包发生的三件事
- 1.产生一个名称空间
- 2.运行包下的
__init__.py 文件,将运行过程中产生的名字都丢带1的名称空间中 - 3.在当前执行文件的名称空间中拿到一个名字xx,xx指向1的名称空间
在python3中,即使包下没有__init__.py文件,import 包仍然不会报错,而在python2中,包下一定要有该文件,否则import 包报错
强调
1.关于包相关的导入语句也分为import和from ... import ...两种,但是无论哪种,无论在什么位置,在导入时都必须遵循一个原则:凡是在导入时带点的,点的左边都必须是一个包,否则非法。可以带有一连串的点,如import 顶级包.子包.子模块,但都必须遵循这个原则。但对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)。
2、包A和包B下有同名模块也不会冲突,如A.a与B.a来自俩个命名空间
3、import导入文件时,产生名称空间中的名字来源于文件,import 包,产生的名称空间的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件
针对包内的模块之间互相导入,导入的方式有两种
1、绝对导入:以顶级包为起始
from demo import versions
from demo.a.m1 import b
import也能使用绝对导入,导入过程中同样会依次执行包下的__init__.py,只是基于import导入的结果,使用时必须加上该前缀
2、相对导入:.代表当前文件所在的目录,…代表当前目录的上一级目录,依此类推
from . import versions
总结
包的使用需要牢记三点 1、导包就是在导包下__init__.py文件 2、包内部的导入应该使用相对导入,相对导入也只能在包内部使用,而且…取上一级不能出包 3、 使用语句中的点代表的是访问属性 m.n.x ----> 向m要n,向n要x 而导入语句中的点代表的是路径分隔符 import a.b.c --> a/b/c,文件夹下a下有子文件夹b,文件夹b下有子文件或文件夹c 所以导入语句中点的左边必须是一个包
**from 包 import ***
在使用包时同样支持from pool.futures import * ,毫无疑问* 代表的是futures下__init__.py 中所有的名字,通用是用变量__all__ 来控制*代表的意思
__all__=['process','thread']
|