模块基础
什么是模块
模块式一系列功能的集合体,而函数是某一个功能的集合体,因此模块可以看成是一堆函数的集合体。一个py文件内部可以放一堆函数,因此一个py文件就可以看成是一个模块。如果这个py文件的文件名为module.py,则模块名为module
- 自定义模块:如果你自己写一个py文件,在文件内写入一堆函数,则它被称为自定义模块,即使用python编写的.py文件
- 第三方模块:已被编译为共享库或DLL的C或C++扩展,如requests
- 内置模块:使用C编写并链接到python解释器的内置模块,如time
- 包:把一系列模块组织到一起的文件夹(注:文件夹下有一个__init__.py文件,该文件夹称之为包)
为什么要用模块
- 用第三方或者内置的模块是一种拿来主义,可以极大地提升开发效率。
- 自定义模块,将我们自己程序中用到的公共功能,写入一个python文件,然后程序的各部分组件可以通过导入的方式来引用自定义模块的功能。
如何用模块
使用import或者from...import...导入模块
import模块名
import time首次导入模块发生了3件事:
- 以模块为准创造一个time模块的名称空间
- 使用python解释器运行对应的文件,将执行过程中产生的名字都丢到time模块的名称空间
- 在当前执行文件中会有一个time变量指向time模块的名称空间
模块的重复导入会直接引用之前创造好的结果,不会重复执行模块的文件,即重复导入会发生:spam=spam=模块名称空间的内存地址
from模块名import具体的功能
from...import...首次导入模块发生了3件事:
- 以模块为准创造一个模块的名称空间
- 使用python解释器运行对应的文件,将执行过程中产生的名字都丢到模块的名称空间
- 在当前执行文件的名称空间中拿到一个名字,该名字直接指向模块中的某一个名字,意味着可以不用加任何前缀而直接使用(from time import sleep:在当前文件中会有一个sleep变量指向time模块名称空间中的sleep)
- 优点:不用加前缀,代码更加精简
- 缺点:容易与当前执行文件中名称空间中的名字冲突
导入文件内所有的功能:
# spam.py
__all__ = ['money', 'read1'] # 只允许导入'money'和'read1'
# run.py
from spam import * # 导入spam.py内的所有功能,但会受限制于__all__
import和from...import...的异同
相同点:
- 两者都会执行模块对应的文件,两者都会产生模块的名称空间
- 两者调用功能时,需要跑到定义时寻找作用域关系,与调用位置无关
不同点
- import需要加前缀;from...import...不需要加前缀
模块的重命名规范
重命名:import module as x
PEP8规范:模块应该分开导入;先导入内置模块,再导入第三方模块,最后导入自定义模块;模块要在程序开头导入
循环导入问题
什么循环导入
# m1.py
from m2 import y
x = 10
# m2.py
from m1 import x
y = 20
为什么会有循环导入问题:
- mi文件需要导入m2文件的y
- m2文件需要导入m1文件的x
- 代码自上而下运行,m1需要m2的y,然后会去m2的名称空间找到y,但是找到y前,需要运行m2的代码,m2的代码第一句是去找m1的x,然后又回去运行m1的代码,m1的第一行代码又去寻找m2的y
- 就是说m1只能运行第一行,m2也只能运行第一行,所以出现了循环导入问题
解决方法一
# m1.py
x = 10
from m2 import y
# m2.py
y = 20
from m1 import x
- 但是这样做,对于所有的变量都需要这样做,才能解决所有问题所以是治标不治本的
解决方法二
# m1.py
def f1():
from m2 import y
x = 10
f1()
# m2.py
def f2():
from m1 import x
y = 20
f2()
名字的执行顺序是:内置(python解释器启动的时候)—>全局(文件执行的时候)——>局部(函数调用的时候)
模块的搜索路径
- 去内存中找
# test.py
import m1 # 从m1.py文件中导入的,然后会生成m1模块的名称空间
import time
# 删除m1.py文件,m1模块的名称空间仍然存在
time.sleep(10)
import m1 # 不报错,一定不是从文件中获取了m1模块,而是从内存中获取的
- 去内置模块中找
# time.py
print('from time')
# test.py
import time # 无任何打印,所以他先去内置模块中找了
- 去环境变量中找
import sys
print(sys.path)
# b/a/m1.py
# b/test.py
import m1 # 报错
sys.path.append('b/a')
import m1
python文件的两种用途
- 模块文件,被当做模块导入,有多个
- 运行文件,被当做执行文件执行,只有一个
- 搜索路径以执行文件为基准
# m1.py
def f1():
print('from f1')
f1()
# test.py
import m1
m1.f1() # 运行两次,结果为‘from f1’‘from f1’
- __name__
# m1.py
def f1():
print('from f1')
if __name__ == '__main__': # __name__在m1.py被当做模块导入时是模块名,作为执行文件时是'__main__'
f1()
# test.py
import m1
m1.f1() # 运行两次,结果为‘from f1’