模块与包管理

模块

"""
常见的四种模块: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

包管理的好处

包管理内部采用相对导入管理模块或者模块中的名字(变量/函数名),外部调用模块的文件采用绝对导入导入一个包名,实际上导入的是路径,这样做的好处是复用性高,外界想要使用包中的模块只要导入包的路径,接下来都不用修改内部代码
posted @ 2019-05-07 01:37  cjw1219  阅读(138)  评论(0编辑  收藏  举报