python package
引言
前提知识:days10-python-modules&& namespace学习
我们已经学习了python 的模块知识,我们也知道了模块就是.py文件,那如果我们有很多模块呢,是将他们放在一个目录下?那如果模块名相互重复了我们要怎么解决冲突呢?还有这个多模块之间肯定会有名称重复呀(函数名重复、全局变量名重复、类名重复等问题),我们如何解决?
要解决上面的问题我们就要引入包:包允许使用点表示法对模块命名空间进行分层结构管理。它有助于避免模块间全局变量名之间的冲突,包也有助于避免模块名之间的冲突。
python package ,其实就是一个目录,用来管理模块的,不过这个目录与我们操作系统的文件夹有点小小的不一样,它 必须包含__init__.py
这样一个模块(文件),这个文件的内容可以为空,但必须有这个文件来标识当前所在文件夹为python package。
包的创建
- 直接通过创建文件夹的方式创建,并在文件夹中添加
__init__.py
文件
通过这种方式创建,要注意你的文件夹所在路径一定要在
sys.path
的路径中呀。否则导入包的时候会提示找不到包哦。
- 通过pycharm的方式创建。
举例:
我通过pycharm的方式创建了名称为package
的包,并在包中添加了mod1.py
、mod2.py
的两个模块
mod1.py
def mod1_func():
print('[mod1] mod1_func()')
mode_var='我是模块1'
mod2.py
def mod2_func():
print('[mod2] mod2_func()')
mode_var='我是模块2'
包的导入
我们可以通过类似于点的方式(pakcage.mod1, package.mod2)来import我们包中的模块,语法其实与模块的导入语法类似。
回忆模块的导入语法:
import <module_name>[, <module_name> ...]
包的导入举例:
工程下新建days11.py
import package.mod1,package.mod2
print(package.mod1.mode_var)
print(package.mod2.mode_var)
package.mod1.mod1_func()
package.mod2.mod2_func()
输出:
我是模块1
我是模块2
[mod1] mod1_func()
[mod2] mod2_func()
当然上面两种方式导致我们使用包中内容十分麻烦,我们要通过包名.模块名.
这样的前缀去使用我们模块中的内容,我们改成from import
的语句
回忆模块的from import
语法:
from <module_name> import <name> as <alt_name>
举例:
from package.mod1 import mode_var as mode1_var ,mod1_func
from package.mod2 import mode_var as mode2_var,mod2_func
print(mode1_var)
print(mode2_var)
mod1_func()
mod2_func()
包也支持下面这种from import
语法
from <package_name> import <modules_name>[, <module_name> ...]
from <package_name> import <module_name> as <alt_name>
示例:
from package import mod1 as model1
from package import mod2
print(model1.mode_var)
print(mod2.mode_var)
model1.mod1_func()
mod2.mod2_func()
__init__.py
结论:该文件的作用就是相当于把自身整个文件夹当作一个包来管理,每当有外部import
包或者包中模块的时候,就会自动执行里面的函数。
我们已经知道我们在模块中我们可以使用from <module_name> import *
,[虽然不建议这样做]。那包是否也有个from <package_name> import *
的语法呢?
试试:
我们知道dir()
是返回当前命名空间的名称列表
列如:
from package.mod1 import *
print(dir())
输出:
[‘annotations’, ‘builtins’, ‘cached’, ‘doc’, ‘file’, ‘loader’, ‘name’, ‘package’, ‘spec’, ‘mod1_func’, ‘mode_var’]
结果是返回了mod1的内容呀
那么:
from package import *
print(dir())
是会返回package下的mod1、mod2名,还是?
结果:
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
无报错说明语法是支持的呀,但关于mod1、mod2什么也没有返回呀。实际上python遵循这样的约定:当我们使用from <package_name> import *
这样的语句的时候,我们必须在__init__.py
中在一个叫__all__
名字的列表中列入你要导入的模块名
举例:
from package import *
print(dir())
mod1.mod1_func()
mod2.mod2_func()
结果:
其实模块中使用import * 也必须在模块中在
__all__
列表中申明什么要导入
那 __init__.py
还可以干些什么呢?
举例我有如下的这种包结构
.
└── package
| ├── subpackage
| │ ├── mod3.py
| │ └── subsonpackage
| │ |__mod4.py
| └── mod1.py
| |__ mod2.py
| |__ __init__.py
|_____ days11.py
模块对应代码如下
#mod1.py
def mod1_func():
print('[mod1] mod1_func()')
mode_var='我是模块1'
#mod2.py
def mod2_func():
print('[mod2] mod2_func()')
mode_var='我是模块2'
#mod3.py
def mod3_func():
print('[mod3] mod4_func()')
mode_var='我是模块3'
##mod4.py
def mod4_func():
print('[mod4] mod4_func()')
mode_var='我是模块4'
试想我要在去days11.py
引用mod1、mod2、mod3、mod4中的函数 ,那我是不是我要写类似下面这种的import 语句, 那如果我不止days10.py要引入,我还有其他文件都要引入,那是不是我每个文件都要写类似下面的这种import语句。
from package.subpackage.subsonpackage import mod4
from package.subpackage import mod3
from package import mod1 , mod2
还有其他办法吗?不想写这么多重复的代码…
尝试一:
我啥都不干,直接导入包是否可行?
不行呀,显然它把我的包当成了模块来处理了…
尝试二 使用__init__.py
修改__init__.py
文件,将我的导包语句放到__init__.py
中呢
__init__.py
from package import mod1,mod2
from package.subpackage import mod3
from package.subpackage.subsonpackage import mod4
days11.py
import package as pg
pg.mod1.mod1_func()
pg.mod2.mod2_func()
pg.mod3.mod3_func()
pg.mod4.mod4_func()
输出:
结果可行啊…
我甚至可以这样玩
__init__.py
from package import mod1,mod2
from package.subpackage import mod3
from package.subpackage.subsonpackage import mod4
func1=mod1.mod1_func
func2=mod2.mod2_func
func3=mod3.mod3_func
func4=mod4.mod4_func
days11.py
import package as pg
pg.func1()
pg.func2()
pg.func3()
pg.func4()
输出:
什么原理?
答: 命名空间 ,在__init__.py
中声明的所有类型和变量,就是其代表的模块的类型和变量。
__init__.py
的设计原则
A、不要污染现有的命名空间。模块一个目的,是为了避免命名冲突,如果你在种用__init__.py时违背这个原则,是反其道而为之,就没有必要使用模块了。
B、利用__init__.py对外提供类型、变量和接口,对用户隐藏各个子模块的实现。
C、只在__init__.py中导入有必要的内容,不要做没必要的运算。