模块的简介
在Python中,一个py文件就是一个模块,文件名为xxx.py模块名则是xxx,导入模块可以引用模块中已经写好的功能。如果把开发程序比喻成制造一台电脑,编写模块就像是在制造电脑的零部件,准备好零部件后,剩下的工作就是按照逻辑把它们组装到一起。
将程序模块化会使得程序的组织结构清晰,维护起来更加方便。比起直接开发一个完整的程序,单独开发一个小的模块也会更加简单,并且程序中的模块与电脑中的零部件稍微不同的是:程序中的模块可以被重复使用。所以总结下来,使用模块既保证了代码的重用性,又增强了程序的结构性和可维护性。另外除了自定义模块外,我们还可以导入使用内置或第三方模块提供的现成功能,这种“拿来主义”极大地提高了程序员的开发效率。
模块分为内置模块、第三方模块、自定义模块
模块导入的方式
import语句
导入模块只需要一次就可以,即使导入多次也只执行一次。
import md
导入模块发生了哪些事呢?
1.首先会运行执行文件,产生执行文件的全局名称空间
2.运行导入文件
3.产生导入文件的全局名称空间,把导入文件中的名字全部放入全局名称空间去
4.会在执行文件中产生一个名字md指向导入文件的名称空间
若要引用模块名称空间的名字,需要加上前缀
import md#引入模块md
a=md.x #引用模块md中的变量x的值赋值给当前名称空间中的名字a
md.get() #引用模块md中的get函数
用import语句导入多个模块,可以写多行import语句
import module1
import module2
...
import moduleN
还可以在一行导入,用逗号分隔开不同的模块
import get,read2,change
但其实第一种形式更为规范,可读性更强,推荐使用,而且我们导入的模块中可能包含有python内置的模块、第三方的模块、自定义的模块,为了便于明显地区分它们,我们通常在文件的开头导入模块,并且分类导入,一类模块的导入与另外一类的导入用空行隔开,不同类别的导入顺序如下
# 1. python内置模块
# 2. 第三方模块
# 3. 程序员自定义模块
当然,我们也可以在函数内导入模块,对比在文件开头导入模块属于全局作用域,在函数内导入的模块则属于局部的作用域。
from ...import...
导入模块也会执行导入文件,多次导入也只会执行一次。
如果使用from...import...句式的情况,在执行文件中会出现名字冲突的情况(在执行文件中出现了和导入的名字一样的时候), 出现冲突的时候离谁近用谁的。
所有的导入语句都写在文件的开头
另外from语句支持from foo import 语法,代表将foo中所有的名字都导入到当前位置。
from foo import * # 把foo中所有的名字都导入到当前执行文件的名称空间中,在当前位置直接可以使用这些名字
a=x
get()
change()
obj=Foo()
如果我们需要引用模块中的名字过多的话,可以采用上述的导入形式来达到节省代码量的效果,但是需要强调的一点是:只能在模块最顶层使用的方式导入,在函数内则非法,并且的方式会带来一种副作用,即我们无法搞清楚究竟从源文件中导入了哪些名字到当前位置,这极有可能与当前位置的名字产生冲突。
模块的其他用法补充
import foo as f #为导入的模块foo在当前位置起别名f,以后再使用时就用这个别名f
f.x
f.get()
循环导入问题
循环导入问题是指一个模块在加载或者导入过程中导入另一个模块,而在另一个模块中又返回来导入第一个模块的名字,由于第一个模块尚未加载完毕,所以引用失败抛出异常。这是因为在python中,同一个模块只会在第一次执行其内部代码,再次导入该模块时,即便是该模块尚未加载完毕也不会重复去执行内部代码。
m1.py
#假设这是m1.py文件
print('正在导入m1')
from m2 import y
x='m1'
m2.py
#假设这是m1.py文件
print('正在导入m2')
from m1 import x
y='m2'
run.py#这是执行文件
import m1
结果1
#1、执行run.py会抛出异常
正在导入m1
正在导入m2
Traceback (most recent call last):
File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/aa.py", line 1, in <module>
import m1
File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module>
from m2 import y
File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m2.py", line 2, in <module>
from m1 import x
ImportError: cannot import name 'x'
#2、分析
先执行run.py--->执行import m1,开始导入m1并运行其内部代码--->打印内容"正在导入m1"
--->执行from m2 import y 开始导入m2并运行其内部代码--->打印内容“正在导入m2”--->执行from m1 import x,
由于m1已经被导入过了,所以不会重新导入,所以直接去m1中拿x,然而x此时并没有存在于m1中,所以报错
结果2
# 1、执行文件不等于导入文件,比如执行m1.py不等于导入了m1
直接执行m1.py抛出异常
正在导入m1
正在导入m2
正在导入m1
Traceback (most recent call last):
File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module>
from m2 import y
File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m2.py", line 2, in <module>
from m1 import x
File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module>
from m2 import y
ImportError: cannot import name 'y'
# 2、分析
执行m1.py,打印“正在导入m1”,执行from m2 import y ,导入m2进而执行m2.py内部代码--->打印"正在导入m2",执行from m1 import x,此时m1是第一次被导入,执行m1.py并不等于导入了m1,于是开始导入m1并执行其内部代码--->打印"正在导入m1",执行from m1 import y,由于m1已经被导入过了,所以无需继续导入而直接问m2要y,然而y此时并没有存在于m2中所以报错
在你编码的过程中,循环导入是不能允许出现的
如果你的代码出现了循环导入问题,那就是你的代码设计不合理,及时更正
执行文件
if __name__ == '__main__':
在这个判断里面写的代码,只有是以执行文件运行的时候,才会被执行,当被当成导入文件的时候,不会被运行
名字的查找顺序
1.先从内存中查找
2.再从内置模块中去找
3.最好去环境变量sys.path中去找
以上都找不到就直接报错
注意,给文件或者模块起名字的时候一定不要和内置模块重名
sys.path中的路径
import sys
print(sys.path)
['D:\\Python27\\day20', 'D:\\Python27\\day20', 'D:\\PyCharm 2021.1.3\\plugins\\python\\helpers\\pycharm_display', 'D:\\Python38\\python38.zip', 'D:\\Python38\\DLLs', 'D:\\Python38\\lib', 'D:\\Python38', 'C:\\Users\\oldboy\\AppData\\Roaming\\Python\\Python38\\site-packages', 'D:\\Python38\\lib\\site-packages', 'D:\\PyCharm 2021.1.3\\plugins\\python\\helpers\\pycharm_matplotlib_backend']
上面的列表中第一个元素永远是当前文件所在的路径
找模块的两种方式
1.把模块所在的路径添加到sys.path中
import sys
sys.path.append(r'D:\Python27\day20\aaa\bbb')
2.利用from...import句式
from aa.bb.cc import m1
相对导入与绝对导入
在导入模块的时候,模块的查找始终是以执行文件所在的路径为准
绝对导入,始终以执行文件所在的sys.path路径为基准查找
相对导入,可以打破始终以执行文件的查找顺序
. 代表的是当前路径
from .ccc import m2
包的使用
随着模块数量的增多,把模块不加区分的放在一起很不合理,于是python为我们提供了种把 模块 组织到一起的方法,就是创建一个包。包就是含有__init__.py文件的文件夹
pool/ #顶级包
├── __init__.py
├── futures #子包
│ ├── __init__.py
│ ├── process.py
│ └── thread.py
└── versions.py #子模块
创建包的目的不是运行,而是被导入使用,包只是模块的一种形式,本质还是模块
包属于模块的一种,首次导入包会做三件事情:
1.执行包下的__init__.py文件
2.产生一个新的名称空间用于存放__init__.py执行过程中产生的名字
3.在执行文件所在的名称空间中得到一个名字pool,该名字指向__init__.py的名称空间 ,例如pool.xxx和pool.yyy中的xxx和yyy都是来自于pool下的__init__.py,也就是说导入包时并不会导入包下所有的子模块与子包
import pool
pool.versions.check() #抛出异常AttributeError
pool.futures.process.ProcessPoolExecutor(3) #抛出异常AttributeError
pool.versions.check()要求pool下有名字versions,而pool.versions下有名字check。但是pool下没有名字versions,这就需要在pool下的__init__.py中导入模块versions