1 什么是模块化编程?
工程模块化是指将具有一定共性的功能封装成一个模块,并对外暴露应用接口,方便其他工程直接调用而无需关注底层实现的思想,工程模块化可以避免工程中各种功能函数相互交杂、定义混乱不堪的情形,有助于提高系统可维护性。
在C/C++中,工程模块化的基础是函数头文件.h,其是专门存放函数声明的文件,这些函数声明的具体实现则分离到函数源文件.cpp或.c中,若干个头文件和源文件组成一个模块。
在Python中,工程模块化的基础则是 __init__.py文件
2 init.py文件的作用
__init__.py
文件有如下作用:
- 组织包,是Package的标识文件
# 目录结构 app |- pkg_1 |- __init__.py |- moduleA.py # fun1() |- moduleB.py |- pkg_2 |- __init__.py |- test.py # pkg_1 __init__.py from pkg_1 import moduleA # test.py中引用 import pkg_1 pkg_1.moduleA.fun1() # 运行成功 # 删除__init__.py运行失败
没有__init__.py就无法导入包
在__init__.py中定义__all__列表控制着包的导入行为
这里解释模糊导入的概念。模糊导入的句式为:from pkg import *
*为通配符,即导入包中的所有模块。若希望为包中的模块设置访问权限,即在模糊导入作用下选择性地引入模块,则需要在__init__.py中定义__all__列表。例如上例中,在pkg_1->__init__.py中定义__all__=["moduleA"],则在模糊导入时只引入模块moduleA而不会导入moduleB。因此__all__的作用是:在模块级别暴露接口,提供了公开(Public)接口的约定。
在__init__.py中导入其他包或模块,方便组织管理各个模块之间的引用
3 Python如何import第三方库
前面说到,Python包以__init__.py为标志,用于实现工程模块化,假设包组织结构的实例如下:
package |- subpackage1 |- __init__.py |- moduleA.py #fun1() fun2() |- subpackage2 |- __init__.py |- moduleA.py #fun1() |- moduleB.py #fun3() fun4()
用虚拟文件夹的方式理解Python包。所有的包都可视作文件夹,其下包含模块或子包(子文件夹),模块中包含函数、类、变量等属性。当前路径位置可视作一个空白文件夹,关键字from理解为“打开”,关键字import理解为“导入”,必须指出:所有import相关操作都要落实到模块或属性。一般地,导入有如下方式:
import subpackage1.moduleA
此方式相当于把一个名为subpackage1的文件夹复制粘贴到当前路径下,文件夹只包含模块moduleA,即使subpackage1中可能还有其他模块,引用moduleA中的func1()需要subpackage1.moduleA.fun1(),即打开subpackage1文件夹,再使用模块moduleA中的属性fun1()。注意,如果仅import subpackage1,相当于只引入了一个空文件夹,此时无法调用fun1(),除非在__init__.py中提前导入了模块。
from subpackage1 import moduleA
此方式相当于打开一个名为subpackage1的文件夹,再将其中的模块moduleA复制粘贴到当前空白文件夹下,引用moduleA的fun1()需要moduleA.fun1()。这种方式下,还有from subpackage1 import *的句式可以引入包中的所有模块。
from subpackage.moduleA import fun1()
此方式相当于打开一个名为subpackage1的文件夹下的模块moduleA,再将其中的fun1()复制粘贴到当前空白文件夹,引用fun1()只需fun1()即可。
除了应用上述导入句式外,还需要注意当前文件的运行路径,如下所示为一个忽略路径因素造成的导入包报错,因为运行目录app\pkg_2\下没有文件pkg_1且环境变量中也不存在pkg_1。
app |- pkg_1 |- __init__.py |- moduleA.py # fun1() |- pkg_2 |- __init__.py |- test.py # test.py中引用 # from pkg_1.moduleA import fun1 # 执行如下 >>> python app\pkg_2\test.py >>> from pkg_1.moduleA import fun1 ModuleNotFoundError: No module named 'pkg_1'
若需要保持运行目录不变,必须进行环境变量配置,在import pkg_1
前先添加父级目录到python解释器的运行环境变量中,在pkg_2
的父级目录app
下可访问到pkg_1
,具体实现上依赖于sys
和os
包
import sys, os sys.path.append(os.path.realpath('..'))
综上所述,包的导入需要考虑两个因素:
- 从哪里导入,即运行路径和环境变量的配置问题;
- 如何导入,即使用何种import句式。