# 今日内容
* 模块的导入
```python
"""
1.什么是模块
模块就是一系列功能的集合体(文件处理中os模块的应用,time模块的应用)
模块有三种来源
1.内置的模块
2.第三方的模块
3.自定义模块
模块的四种表现格式
1.使用python编写的py文件
2.已被编译为共享库或DLL的C或C++扩展
3.把一系列模块组织到一起的文件夹(文件夹下有一个__init__.py文件,该文件夹称之为包)
4.使用C编写并连接到python解释器的内置模块
2.为何要用模块
1.使用内置的或者第三方模块的好处是:拿来主义,极大提升开发效率
2.使用自定义模块的好处是:抽取程序中多个地方都需要用到的公共的功能定义成模块减少代码冗余
(程序是多文件的情况下,将多个文件都需要用到的功能放到一个地方,供大家统一查找)
3.如何使用模块
大前提:一定要区分开谁是执行文件,谁是被导入的模块
"""
```
#### import使用
```python
"""
新建两个py文件一个执行一个被导入
"""
# md.py
print('from the md.py')
money = 1000
def read1():
print('md',money)
def read2():
print('md模块')
read1()
def change():
global money
money = 0
# run.py
money = 66
import md # 文件名是md.py而模块名则就是md
# 右键运行,执行了md文件,多次导入并不会多次运行
"""
1.首次导入模块发生的3件事情
1.会产生一个模块的名称空间
2.执行文件md.py,将执行过程中产生的名字都放到模块的名称空间中
‘money’:1000的内存地址
‘read1’:read1函数的内存地址
‘read2’:read2函数的内存地址
‘change’:change函数的内存地址
图示理解效果更佳
3.在当前执行文件的名称空间中拿到一个模块名,该名字指向模块的名称空间
即:
既然运行了md文件,那么肯定会创建一个名称空间,将md文件中所有的名字与值的绑定关系存起来
run文件也运行了,那么肯定也会创建一个名称空间,将money与md存放进去
‘money’:66的内存地址
‘md’:md.py的名称空间地址
2.之后的导入都是直接引用第一次导入的成果,不会重新执行文件
"""
# 1.执行文件中访问模块名称空间中的名字的语法: 模块名.名字
print(md.money) # 指名道姓的跟md文件要名字money,肯定不会跟当前执行文件中的名字冲突
money # 向当前执行文件的名称空间要名字money
print(md.read1)
print(md.read2)
print(md.change) # 打印获取到的是函数的内存地址 >>> 加括号执行的冲动
# 一次调用read1,read2,change反复明确查找名字的规律
md.read1() # 找的是md文件中的money
md.read2() # 找的是md文件中的money
md.change() # 修改的也是md文件中的money
# 一行导入多个模块(不推荐)
import os,sys,time
# 给导入的模块名起别名
import xxxyyy as xy
"""
import导入模块总结:
在使用的时候必须加上前缀: 模块名.
优点:指名道姓的向某一个名称空间要名字,肯定不会与当前名称空间中的名字冲突
缺点:但凡要用模块中的名字都需要加上模块前缀
"""
```
### from…import...
```python
# 仍然以上述两个文件为例
from md import money
from md import money
"""
from...import...也发生了三件事,前两件跟import导入一样,仅仅是最后一件有点区别
1.会产生一个模块的名称空间
2.执行文件md.py,将执行过程中产生的名字都放到模块的名称空间中
3.在当前执行文件中直接拿到一个名字,该名字就是模块中相对应的名字
‘money’:md文件中money
"""
# 1.
money = 888
from md import money
# 2.
from md import money
money = 888
# 总结
"""
优点:使用时无需再加前缀,更简洁
缺点:可能会与当前名称空间中的名字冲突
"""
from md import read1
money = 999
read1() # 函数调用在定义阶段就已经固定死了,与调用位置没有关系,这里修改的也仅仅是执行文件名称空间中的名字!!! 图示理解
# 再举例佐证
chang() # 修改的是模块中的money指向的值,与执行文件中的money没有任何关系
print(money)
# 了解
from md import * # *代表从被导入模块中拿到所有的名字(不推荐使用),因为很有可能与当前名称空间中的名字器冲突
# __all__补充 *导入的就是__all__里面列出来的所有的名字
```
### 模块的循环导入
```python
"""
模块
m1.py
print('正在导入m1')
from m2 import y # 第一次导入要发生的三件事
x = 'm1'
m2.py
print('正在导入m2')
from m1 import x
y = 'm2'
运行文件
run.py
import m1 # 第一次导入要发生的三件事
"""
# 逐步分析,先执行run,产生run对应的名称空间
# 导入m1,由于是首次导入会创建一块m1对应的名称空间
# 运行m1中的代码,发现m1中又导了m2,m2也是首次导入
# 创建m2的名称空间,执行m2中的代码
# m2中需要找m1中的x,这个时候x还没有造出来
"""
这个现象就是循环导入的问题,m1导了m2,m2又反过来导了m1
循环导入的出现意味着你的程序设计的不合理,需要重新设计
"""
# 解决循环导入带来的问题
# 方式1:把循环导入的语句放到名字定义的后面
print('正在导入m1')
x = 'm1'
from m2 import y
print('正在导入m2')
y = 'm2'
from m1 import x
# 方式2:将循环导入语句放到函数内
print('正在导入m1')
def f1():
from m2 import y
print('m1.f1>>>y:',y)
x = 'm1'
print('正在导入m2')
def f2():
from m1 import x
print('m2.f2>>>x:',x)
y = 'm2'
# 图示理解
# 总结:在导入之前,先让其把名字准备就绪
```
#### 判断py文件被当作模块导入还是执行文件
```python
if __name__ == '__main__': # 当文件被直接执行时
if __name__ == 'py文件名' # 当文件被导入时
```
### 模块的查找顺序
```python
"""
模块的查询顺序
1.先从内存中找可能已经加载了的
2.内置模块
3.sys.path列表里面每一个路径下去找
需要知道sys.path列表中第一个路径就是当前被执行文件所在的文件夹
ps:python也支持直接从zip压缩包中导入模块
"""
# 验证第一点,找模块先从内存中找看看要找的模块是不是已经加载了
"""
定义一个模块,在另一个文件中导入,然后执行文件中主动睡眠10s,期间快速讲模块文件删除,发现执行文件睡眠之后的代码仍然可以访问
但是一旦程序结束运行,再次执行就会报错了
import time
import m1
time.sleep(10)
import m1
m1.f1()
"""
# 需要注意的是你新建的py文件名最好不要与已经存在的模块名冲突 time模块为例
```
#### 稍微复杂一点的情况
```python
# 要导的模块在一个文件夹内,执行文件与文件夹同级跟模块文件不同级
import md. # 保存,因为内存,内置都没有,sys.path也是以当前执行文件所在的文件夹找文件
# 要想解决,需要利用sys.path.append()将模块所在的文件夹添加进去
# 不想用上面的方式解决,可以用以下方式
from ... import ...
from dir1.dir2 import md
```
### 模块的绝对导入
```python
# 再次强调:模块导入一定要分清楚执行文件和被导入文件,sys.path依据的是被执行文件所在文件夹
-模块的绝对导入
-dir1
-m1.py
-m2.py
-run.py
from dir1 import m1 # 执行文件run.py所在的文件夹是"模块的绝对导入",以该文件夹为基准可以找到dir1进而可以找到里面的m1
# 再来一个迷惑人的例子
-模块的绝对导入
-dir1
-m1.py
-m2.py
-run.py
# m2中直接导m1
# 再温习一遍
-模块的绝对导入
-dir0
-dir1
-m1.py
-m2.py
-run.py
# 所有文件的模块导入都是以执行文件为准
# 可以利用sys.path.appen将dir0添加到环境变量中
"""
总结:以执行文件开始(依据sys.path)一层层找关系,叫绝对导入
优点:执行文件与被导入的模块中都可以使用
缺点:所有的导入都需要以sys.path为起始点,导入麻烦
"""
```
#### 模块的相对导入
```python
"""
相对导入:参照当前所在的文件的文件夹为起始开始查找
.代表当前所在文件的文件夹
..代表上一级文件夹
...代表上一级的上一级文件夹
优点:导入更加精简
缺点:只能在被导入的模块中使用,不能在执行文件中用
"""
# 但是需要注意相对导入只能在被导入的模块文件夹中使用,不能在执行文件中使用
# 包
讲模块的时候讲过模块的三种来源,四种表现格式
```python
模块有三种来源
1.内置的模块
2.第三方的模块
3.自定义模块
模块的四种表现格式
1.使用python编写的py文件
2.已被编译为共享库或DLL的C或C++扩展
3.把一系列模块组织到一起的文件夹(文件夹下有一个__init__.py文件,该文件夹称之为包)
4.使用C编写并连接到python解释器的内置模块
# 模块和包的由来介绍
"""
1.什么是包
包就是一个包含有__init__.py文件的文件夹
本质就是模块,也能够被导入,包内部包含的文件也都是用来被导入使用的
2.为什么要有包
文件夹是用来更好的管理文件的,而包就是方便模块的设计者更好的组织模块
"""
# 建一个包含一个__init__.py的文件夹(p1) 另建同级别的py文件导入该文件夹分析流程
"""
回忆导入模块的三件事
1.产生一个模块的名称空间
2.执行模块文件中的代码,将执行过程中产生的名字都丢到名称空间中
3.在当前执行文件中拿到一个名字,该名字就是指向模块的名称空间
疑问:导入模块第二步是执行文件的代码,现在是一个文件夹,那么执行谁呢丢给谁呢?
推导出导入包的三件事
1.以包下的__init__.py文件为基准来产生一个名称空间
2.执行包下的__init__.py文件的代码,将执行过程中产生的名字都丢到名称空间中
3.在当前执行文件中拿到一个名字p1,该p1就是指向__init__.py的名称空间
"""
# 验证python3中如果文件夹里面不含__init__.py也不会报错
# 切换到python2中发现直接报错
# 即python2中包里面必须有__init__文件,python即便没有也不会报错
# 验证导入包就是指向__init__.py文件
# 访问模块中的名字,其实就是去__init__.py文件中去找该名字
```
#### 查找名字
```python
# 根据上面的推导 导包其实就是指向__init__.py文件 访问该文件里面的名字,那这样还不如直接建一个py文件
# 导入包是想找到包里面所有的py文件中的名字,而不仅仅是__init__文件
"""
不要绕来绕去,讲的过于复杂,直接分两个角度去讲即可
第一个角度:包的使用者
包的使用者在下载完了包之后,只需要确保在他的程序里面能够正常的找到包所在路径并导入即可
即利用sys.path来操作环境变量
将包所在的上一级文件夹路径添加到环境变量中(使用者是需要知道下载模块或者包到底在哪个文件夹下,就跟你下载一个软件一样,你肯定得知道下到哪里去了)
导入语句中.的左边肯定必须是一个包
第二个角度:包的设计者
确保自己写的所有的模块都能够被顶级的包__init__.py找到,利用模块的相对导入
好处有亮点:
1.模块改名字,不影响内部查找
2.不会被一层层的嵌套关系和执行文件导入文件的概念所混淆
"""
```