模块与包管理
模块
"""
常见的四种模块:module
1.使用python编写的.py文件
2.把一系列模块组织到一起的文件夹(注:文件夹下有一个__init__.py文件,该文件夹称之为包)
3.使用C编写并链接到python解释器的内置模块
4.已被编译为共享库或DLL的C或C++扩展
"""
# 1、什么是模块:一系列功能的集合体,模块都有'.'语法
任何一个py文件或者说数据结构都能称为模块,因为都有'.'语法
# 2、为什么会出现模块:很多相似的功能,要统一管理,将这些功能放在一个文件中,该文件就是管理这些功能的集合体,我们命名为模块
# 3、怎么使用模块
# -- 在要使用模块功能的文件中导入模块:
import 模块名 - 模块名:用来管理一系列功能的文件名(实现功能的文件名)
# 4、在哪使用模块
# -- 在所有要使用模块中功能的文件中导入并使用模块
#模块文件(m1.py)
def f1():
print('扫地')
def f2():
print('洗衣')
def f3():
print('拖地')
# 使用模块文件
import m1
m1.f1()
m1.f2()
m1.f3()
导入模板完成的三件事
# 首次导入
import 模块名
# 1.将被导入的模块编译形成对应的pyc文件
# 2.进入模块,从上至下执行模块中的代码,将产生的所有名字(变量)存放在该模块文件的全局名称空间中
# 3.在使用模块的文件中,产生一个与模块名(模块文件名)同名的名字(变量),指向模块的全局名称空间,这个名字可以称为文件对象或者模块对象
#文件对象/模块对象
代表模块,指向的是模块文件里存储对象的名称空间的地址,通过使用'.'语法去使用模块里的东西
# 模块文件 m1.py
print(1)
print(2)
print(3)
a=10
def fn():
print(a)
print('end')
#使用模块的文件
import m1
print(m1.__dict__) #打印m1的名称空间
print(m1.__dict__['a']) #打印m1的名称空间
print(m1.a) # m1文件对象通过'.'语法去使用模块里的变量a
print('t1 end')
print(m1) #打印的是m1这个文件对象指向的模块的地址
# 再次导入
# 不再执行导入模块的前两步,只会走第三步:在当前模块中产生一个名字,指向第一次导入在内存中产生的全局名称空间
import m1
print(m1.a)
print(m1.__dict__) #再次导入模块m1, m1的地址指向模块的名称空间
起别名
import 模块名 as 别名
# 重点:导入一旦起别名,原模块名变量失效,本质只产生了别名变量指向模块文件的全局名称空间
模块的分类
# 大方向:内置(built-in) | 自定义(系统提供 | 第三方提供 | 自己自定义)
模块的加载顺序
# 内存 > 内置 > sys.path (安装环境变量中路径的先后顺序逐一加载)
# 自定义模块加载顺序: 根据环境变量中路径的顺序依次执行,加载到了就停止,所以排在前面的路径下的自定义模块先加载
环境变量
存放路径的list,第一位默认一定是当前执行文件所在的路径
#举例
>>> part4/t1.py
import sys
print(sys)
print(sys.path)
# ['/Users/cj/Desktop/pycharm_project_2/part4', '/Users/cj/Desktop/pycharm_project_2', '/Applications/PyCharm.app/Contents/helpers/pycharm_display', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages', '/Applications/PyCharm.app/Contents/helpers/pycharm_matplotlib_backend']
>>> part3/t1.py
import sys
print(sys)
print(sys.path)
# ['/Users/cj/Desktop/pycharm_project_2/part3', '/Users/cj/Desktop/pycharm_project_2', '/Applications/PyCharm.app/Contents/helpers/pycharm_display', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages', '/Applications/PyCharm.app/Contents/helpers/pycharm_matplotlib_backend']
import sys
# sys.path就是环境变量
# 清空环境变量:所有导入都不能使用了
sys.path.clear()
# 添加指定路径到环境变量
sys.path.append()
sys.path.insert()
from...import语法导入
# 导入完成的三件事
# 1.将被导入的模块编译形成对应的pyc文件
# 2.进入模块,从上执行执行模块中的代码,将产生的所有名字存放在该模块文件的全局名称空间中
# 3.在导入模块的文件中形成(一个或多个)名字指向模块全局名称空间中的(一个或多个)具体名字
from 模块名 import 模块中的名字1, ..., 模块中的名字n
from 模块名 import 名字 as 别名
# from...import导入依赖环境变量 sys.path
#添加环境变量
import sys
sys.path.append(r'/Users/cj/Desktop/pycharm_project_2/part2/t1.py')
from...import *
# 模块中默认会添加 __all__ ,__all__ 就是管理模块中能被 * 导入的变量们
# __all__可以自定义,自定义 * 能导入的变量们, __all__的list中名字全部可以自定义
__all__ = ['a', 'b', 'c', 'd_', '_e']
# 系统默认添加的__all__中不会纳入 _开头的名字
# -- 所以默认在外界通过from...import *无法导入_开头的名字
# -- _开头的名字对from...import *是隐藏的,指名道姓依然可以被外界导入使用
#m1.py
_c=2
a=1000
#t1.py
from m1 import *
print(a)
print(_c) #报错 NameError: name '_c' is not defined
#m1.py
_c=2
a=1000
#t1.py
from m1 import _c
print(_c) #输出c的值
链式导入
# t1导入m1,m1导入m2,m2导入m3
# 执行流程:右键执行t1,在t1导入m1模块的地方直接进入m1,去执行m1,同理在m1执行过程中遇到导入m2,会马上进入m2,去执行m2,一直到m3,m3执行完毕,会回到m2中导入m3的语句,接着往下执行m2,m2执行完毕回到m1,以此类推返回到t1
# 在整个执行流程中,遇到任何模块的二次导入,都是直接引用内存中的名称空间,不会再次进入模块
#t1.py
print('t1 start')
import m1
print('t1 end')
#m1.py
print('m1 start')
import m2
print('m1 end')
#m2.py
print('m2 start')
import m3
print('m2 end')
#m3.py
print('m3 start')
import m1
import m2
print('m3 end')
循环导入
# 循环导入的问题点:名字没有产生就使用名字
# 解决循环导入:先产生名字,在导入模块
# -- 将会产生循环导入的模块,导入语法延后 - 延后导入
# 问题
#t1.py
import m1
# m1.py
import m2
print(m2.y)
x=666
# m2.py
import m1
print(m1.x)
y=888
# 解决
#t1.py
import m1
# m1.py
x=666
import m2
print(m2.y)
# m2.py
import m1
y=888
print(m1.x)
sys.path显示
# 在pycharm中,sys.path环境变量这个列表索引0号位显示的是当前文件所在的路径,索引1号位显示的是pycharm中的项目的根目录
import sys
print(sys.path[0]) # /Users/cj/Desktop/pycharm_project_3/part1/b/c
print(sys.path[1]) # /Users/cj/Desktop/pycharm_project_3
# 但是在命令行界面,sys.path并不会显示项目的根目录
cj@cjwdeMacBook-Pro pycharm_project_3$ python3 /Users/cj/Desktop/pycharm_project_3/part1/t1.py
['/Users/cj/Desktop/pycharm_project_3/part1', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages']
跨文件导入模块分析
# 需求:跨文件夹访问
#目录结构
part1
-> t1.py
->a(ma.py)
->b->c(mc.py)
#打开pycharm时,当前执行文件的环境变量是会被自动创建为这个文件所在的目录,所以如果import 该文件所在的目录的任意模块文件都是可以的
#有时候我们要导入当前文件所在的目录的同级目录的模块文件,或者是同级目录以下的模块文件,这个时候可以将路径加入环境变量或者通过'.'语法进行层级导入,这本质上就是一个绝对路径,起始路径是从环境变量中定义的路径开始的,如果后面需要print输出这个值,那么后面也是要写除了环境变量定义的路径外的所有路径,这种情况定义别名更方便
(方法一) 绝对导入(将文件所在的目录的路径拷贝到环境变量sys.path)
import sys
# 访问模块文件ma里的变量aaa
sys.path.append(r'/Users/cj/Desktop/pycharm_project_3/part1/a')
import ma
print(ma.aaa)
# 访问模块文件mc里的变量ccc
sys.path.append(r'/Users/cj/Desktop/pycharm_project_3/part1/b/c')
import mc
print(mc.ccc)
方法(二) 导入时写绝对路径
# 访问模块文件ma里的变量aaa
import a.ma
print(a.ma.aaa)
# 访问模块文件mc里的变量ccc
import b.c.mc
print(b.c.mc.ccc)
方法(三)通过from...import 直接访问到模块里的变量,基于方法二
# 访问模块文件ma里的变量aaa
from a.ma import aaa
print(aaa)
# 访问模块文件mc里的变量ccc
from b.c.mc import ccc
print(ccc)
#报错信息 ('.'左侧为文件)
import a.ma.aaa #语法错误
ModuleNotFoundError: No module named 'a.ma.aaa'; 'a.ma' is not a package
#报错信息 ('.'左侧为文件)
from b.c import mc.ccc as ccc #语法错误
SyntaxError: invalid syntax
#结论:
导模块中的'.'语法左侧必须为文件夹
import 后面最后只能导入到文件,可以是import <文件夹>.<文件>,但是不能是import <文件>.<文件>
from...import 中的import后面可以导入文件中的具体名字(变量名/函数名)
跨文件夹导入模块总结
# 1.如果a文件夹所在目录在环境变量,a文件夹中的ma模块可以被以下方式导入
import a.ma | from a import ma
# 2.如果a文件夹所在目录在环境变量,a文件夹中的b文件夹的mb模块可以被以下方式导入
import a.b.mb | from a.b import mb
# 3.如果a文件夹所在目录在环境变量,a文件夹中的b文件夹的mb模块中的bbb名字要被直接导入
import a.b.mb.bbb # 错误:所有.左侧必须是文件夹 *****
from a.b import mb.bbb # 错误:所有.左侧必须是文件夹
from a.b.mb import bbb # 正确
模块的两种被执行方式
# 总结:
# 1.一个py文件作为自执行文件,__name__变量的值为 '__main__'
# 2.一个py文件作为模块被导入执行,__name__变量的值为 '文件(模块)名'
# 3. __name__的值始终是字符串
# 4. pycharm中直接输入main就能快速出现 if __name__ == '__main__':
# 如何区别两种方式,可以让一个文件可以自执行,也可以被导入执行 共存
# 在这样的py文件中写:
if __name__ == '__main__':
# 自执行的逻辑 => 因为在文件作为模块使用时 __name__为文件名,不满足条件
pass
#案例 fn3()函数所在的文件被外部调用时候不执行,外部调用时候只执行2个函数fn1(),fn2()
# m1.py
def fn1():
print('fn1 run')
def fn2():
print('fn2 run')
def fn3():
fn1()
fn2()
print('fn3 run')
if __name__ == '__main__':
fn3()
#t1.py
from m1 import fn1,fn2
fn1()
fn2()
包
模块:一系列功能的集合体
包: 一系列模块的集合体,用文件夹来管理一系列有联系功能的模块
# 包 与 普通文件夹存在区别:包的文件夹中一定存在一个__init__.py文件
# -- py2:必须创建 py3:建议创建,不创建系统会自动创建
# __init__.py文件
# 1)产生一个全局名称空间,提供给包的,就代表包的名称空间
# 2)管理 包可以直接点出来使用的 名字
如果没有__init__文件,在py3中会自动优化生成一个名称空间,存储一些系统的内置变量,但是不会文件夹下面模块里定义的名字(变量名/函数名),py2中则直接报错
#(案例1)
文件结构:
part5
>mp(m1.py/m2.py)
>t1.py
import mp #如果没有init文件, mp的名称空间是无法产生m1,m2模块以及他们的名字的对应关系
# 需求: 可以直接通过包名使用包中的某一个模块
print(mp.m1.a)
print(mp.m1.b)
print(mp.m1.c)
# 需求: 可以直接通过包名使用包中的某个模块的名字
print(mp.x)
print(mp.y)
print(mp.z)
# (案例2)
文件结构:
part3
->mp(m1.py, m2.py, m3.py)
->t1.py
(python2 环境中)
#t1.py
import mp
print mp
报错信息:ImportError: No module named mp
(python3 环境中)
import mp
print(mp)
<module 'mp' (namespace)> # mp自己的名称空间为空
print(mp.__dict__) # python3自动创建一个名称空间,都是系统内置的变量的对应关系
# 导包完成的三件事
# 1)编译形成包中__init__.py文件的pyc文件
# 2)执行__init__.py文件,形成一个全局名称空间,将__init__.py文件中所有名字存放其中,该名称空间就代表包的名称空间
# 3)在导包的文件中,产生一个与包名相同的名字,指向包的名称空间(__init__.py文件的全局名称空间)
相对导入
需求1 : 可以直接通过包名使用包中的某个模块的名字
文件结构:
part3
->mp(m1.py, m2.py, m3.py,__init__.py)
->t1.py
(方法一) 直接将值存入__init__.py 文件
#t1.py
# 可以直接通过包名使用包中的某个模块的名字
print(mp.x)
print(mp.y)
print(mp.z)
# __init__.py
x=111
y=222
z=333
(方法二) 将m1导入__init__.py 文件
#t1.py
# 可以直接通过包名使用包中的某个模块的名字
import mp
print(mp.m1.a)
print(mp.m1.b)
print(mp.m1.b)
print(mp.m1.c)
# __init__.py
from . import m1
# '.'就是mp
需求2: 可以直接通过包名使用包中的某个模块的名字
#t1.py
import mp # 导入一个绝对路径/Users/cj/Desktop/pycharm_project_3/part5/mp,所以__init__文件中可以使用相对路径,相当于__init__文件中再导入模块都是基于这个路径导入了
print(mp.x)
print(mp.y)
print(mp.z)
# __init__.py
from .m2 import x
from .m2 import y
from .m2 import z
# '.'就是mp
需求3:直接通过包名使用包中子包模块中的名字
文件结构:
part3
->mp(m1.py, m2.py, m3.py,__init__.py)--> subp(s1.py, __init__.py)
->t1.py
# subp/s1.py
sss=8888
#mp/__init__.py
from .subp.s1 import sss
from .subp import s1
#t1.py
print(mp.sss)
print(mp.s1.sss)
总结
1. mp是一个包,下面存着各个模块文件,运行文件中只要是导入了 import mp,则只要是__init__ 文件里面导入的模块,都能使用;
__init__ 文件中导入模块可以使用相对路径,可以用'.'代替mp
2. import mp ----> 执行文件下相当于导入一个路径 /Users/cj/Desktop/pycharm_project_3/part5/mp
所以__init__文件中可以使用相对路径,相当于__init__文件中再导入模块都是基于这个路径导入了
3. from...import... 格式的文件,后面只能跟一个文件夹或者文件或者名字,不能跟多个字文件夹,文件,名字,用'.'分隔
from . import m1 #正确
from .m2 import x #正确
from .m2 import y #正确
from . import m2.z #错误
直接使用包中模块
# 如果只是想作为普通文件夹,py3中可以省略__init__文件,本质上文件夹也是包
# 导入的手段:1.指名道姓到某一个模块 | 2.指名道姓到某一个模块中的名字
包的管理
#在包中管理模块,或者模块与模块直接功能相互使用,通过from .开头 import 语法进行导入,这种导入叫相对导入
# 在包的__init__.py文件或是包中任意一个模块中
# . 代表当前文件所在目录
# .. 代表当前文件所在目录的上一级目录
# 注:.语法不能出包,因为包外的文件都能自执行,但拥有.开头导入的文件不能自执行
文件结构:
part3
->mp(m1.py, m2.py, m3.py,__init__.py)--> subp(s1.py,s1.py,s3.py __init__.py)
->t1.py
# subp/s1.py
sss=8888
# subp/s2.py
res=99999
# subp/s3.py
from .s2 import res
from ..subp.s1 import sss
def fn():
# 要使用s2中的res
print('s3 fn run')
print(res)
#要使用s1中的sss
print(sss)
#mp/__init__.py
from .subp.s3 import fn
# t1.py
mp.fn()
# __name__ 代表路径
文件结构:
part3
->mp(m1.py, m2.py, m3.py,__init__.py)--> subp(s1.py,s1.py,s3.py __init__.py)
->t1.py
# subp/s1.py
sss=8888
# subp/s2.py
res=99999
# subp/s3.py
from .s2 import res
from ..subp.s1 import sss
def fn():
# 要使用s2中的res
print('s3 fn run')
print(res)
#要使用s1中的sss
print(sss)
# print(__name__)
#mp/__init__.py
from .subp.s3 import fn
# t1.py
mp.fn()
# __name__ 最后的输出结果 mp.subp.s3
包管理的好处
包管理内部采用相对导入管理模块或者模块中的名字(变量/函数名),外部调用模块的文件采用绝对导入导入一个包名,实际上导入的是路径,这样做的好处是复用性高,外界想要使用包中的模块只要导入包的路径,接下来都不用修改内部代码