什么是包?
包就是一系列模块的集合。本质上就是一个包含了__init__.py文件的文件夹。可以通过pycharm直接创建一个包,也可以自己手动创建一个文件夹,然后再在文件夹里面创建一个__init__.py文件。
包有什么作用
和模块类似,包就是为了在文件级别上面管理我们创建的模块。
包的使用
包本质上就是一个模块,因此它的导入和模块的导入方法是完全一样的。不一样的是包内__init__.py的导入方式。
包的注意事项:
#1.关于包相关的导入语句也分为import和from ... import ...两种,但是无论哪种,无论在什么位置,在导入时都必须遵循一个原则:
# 凡是在导入时带点的,点的左边都必须是一个包,否则非法。可以带有一连串的点,如item.subitem.subsubitem,但都必须遵循这个原则。
# 但对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)。 #2、import导入文件时,产生名称空间中的名字来源于文件,import 包,产生的名称空间的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件 #3、包A和包B下有同名模块也不会冲突,如A.a与B.a来自俩个命名空间
为什么要有__init__.py文件
我们之前已经解释过了,导入模块有三个步骤,当我们的模块是一个包的时候,我们就会发现到第二步的时候就没法执行了,因为包就是一个文件夹,怎么去执行呢?因此为了使包也可以导入的时候执行,创建了一个__init__.py文件,执行包的时候其实就是执行包下面的__init__.py文件了。然后我们通过__init__.py文件把包内的模块都导入进来,这样我们就可以使用包内的模块了。
1. 执行文件 创建一个命名空间 2. 执行模块文件,创建模块的命名空间
3. 执行文件创建一个名称执行模块的命名空间
验证一:在__init__.py中导入包内的模块的两种模式(此处和正常的导入模块是有区别的)
例如一个包名为:package 两种方法对于上层的调用都是一样的,都是直接体现为m1 方法一 import package.m1 方法二 from package import m1
验证二: 验证导入包的时候就是导入包下面的__init__.py文件
# 这个是一个包pack下面的init文件 print('我是包下面的init文件!') # 这个是测试包的文件 import pack # 输出结果是 # 我是包下面的init文件! 这就说明当我们导入包名称的时候,其实执行的是__init__文件
验证三: 验证通过导入包执行模块并不会加入到sys.path的路径中
# 这个是pack包下面的一个m1模块 def sayhi(): print('hi i am sayhi!') # 这个是一个包pack下面的init文件 print('我是包下面的init文件!') import m1 # 这个是测试包的文件 import pack # 输出会报错 但是包_init__.py文件修改成下面就不会报错 # 这个是一个包pack下面的init文件 print('我是包下面的init文件!') import pack.m1 这是因为当我执行了测试文件的时候,它只会把当前目录加入到sys.path中, 所以当我们通脱__init__文件去导入m1的时候,系统找不到这个文件,所以报错了
验证四: 验证在__init__.py文件中通过import和from导入的效果是一样的
# 这个是一个包pack下面的init文件 import sys print('我是包下面的init文件!') import pack.m1 for i in sys.modules: print(i) # 这个是一个包pack下面的init文件 import sys print('我是包下面的init文件!') # import pack.m1 from pack import m1 for i in sys.modules: print(i) # 上面两种出现的结果都是 pack pack.m1 结论: 1. 在包的__init__.py文件中import和from的行为都是一致的
2. 两种导入方式,都是在当前空间存储了一个pack.xxx
3. 但是为了用户方便,在调用的时候狐狸了pack的存在
相对导入和绝对导入
绝对导入与相对导入 # 绝对导入: 以执行文件的sys.path为起始点开始导入,称之为绝对导入 # 优点: 执行文件与被导入的模块中都可以使用 # 缺点: 所有导入都是以sys.path为起始点,导入麻烦 # 相对导入: 参照当前所在文件的文件夹为起始开始查找,称之为相对导入 # 符号: .代表当前所在文件的文件加,..代表上一级文件夹,...代表上一级的上一级文件夹 # 优点: 导入更加简单 # 缺点: 只能在导入包中的模块时才能使用 #注意: 1. 相对导入只能用于包内部模块之间的相互导入,导入者与被导入者都必须存在于一个包内 2. attempted relative import beyond top-level package # 试图在顶级包之外使用相对导入是错误的,言外之意,必须在顶级包内使用相对导入,每增加一个.代表跳到上一级文件夹,而上一级不应该超出顶级包
绝对导入实例:
lianxi(目录) good_package(包下面有两个模块和一个包) __init__ from good_package.m1 import * from good_package.m2 import * from good_package.subpack import * m1(模块1) def f1() def f2() m2(模块2) def f3() def f4() subpack(包) __init__ from good_package.subpack.m3 import * m3(模块3) def f5() test(执行文件) # 需求导入包aaa import good_package f1() f2() f3() f4() f5()
把此实例改成相对导入
lianxi(目录) good_package(包下面有两个模块和一个包) __init__ from .m1 import * from .m2 import * from .subpack import * m1(模块1) def f1() def f2() m2(模块2) def f3() def f4() subpack(包) __init__ from .subpack.m3 import * m3(模块3) from ..m1 import * def f5() f1() test(执行文件) # 需求导入包aaa import good_package f1() f2() f3() f4() f5()