软件项目越来越大,复杂之后,模块会越来越多,我们需要对这些模块进行分门别类进行维护、管理,提出了包的概念。
包在目录结构上,往往是一个目录里包含多个模块子目录
myproj
├── cmdb # 代码目录
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── test.py
│ └── views.py
├── manager.py # 主入口运行文件
└── proj # 配置文件目录
├── settings.py
├── urls.py
└── wsgi.py
这里面的cmdb/proj都可以称之为包(package)。下面讲下同一个包里模块的相互调用以及不同包之间的模块的调用方式
- 同一个包(目录)里模块的调用
注意,在python2中有点不同,跨目录调用时,被调用模块目录里必须要有一个__init__.py的空文件,否则找不到对应模块。(其实Python3里没有这个文件的时候虽然可以找到模块,但是只能称作目录,还不是规范的包。)
- 不同包里的模块的相互调用
对于from语句加载包的一些规则:
1. 从当前目录开始一路加载到包,然后导入模块
但是这种方式的问题很明显,当在别的目录或别的机器上执行脚本时就出问题了。
2. 相对路径导入:
利用类似Linux中的".."相对路径的方式进行跨包导入。比如views.py想导入proj/settings.py模块,可以写"from ..proj import settings"。但是利用这种导入会有一个问题,就是当导入的路径要经过最上层路径时会报错"ValueError: attempted relative import beyond top-level package"。比如这里举的例子就会报错,因为cmdb/proj都是顶层目录了。一般实际使用中都是会跨最上层目录的,所以这种方式的弊端过于鸡肋,推荐使用下面介绍的方法。
3. 动态获取路径的方式(通用):
利用Linux中相对路径的概念:我们知道项目打包里面的目录是固定不变,不同包之间模块的调用借用相对路径。本方法原理就是使用sys.path环境变量,需要导入哪个模块,就将其目录加入到环境变量中。
先了解os模块的获取路径的几个方法:
__file__:任何一个脚本程序执行时环境变量dir()中都有这么一个变量(交互式环境下没有),它代表你执行时输入的程序的路径。
os.path.dirname(filename):代表文件的绝对目录
os.path.dirname():表示一个文件or目录的上一层目录,即父目录
sys.path:表示环境变量,里面默认有当前目录,site-package等几个目录。这里的“当前目录”得好好理解。在交换式环境下,这个值为空;
在本例中,它的值是你所处在的Linux的当前目录(/root/pystu/module2/practise/myproj)加上命令行中的目录(cmdb)合并而成的。这个值发现最终都是相当于文件绝对路径的目录部分,即sys.path[0] == os.path.dirname(os.path.abspath(__file__))
根据上面的案例,假设test.py里面想加载settings.py模块,这是属于跨了包(目录)。而如果是直接执行test.py作为入口,sys.path的当前路径下是找不到settings模块的。思路如下:
import os,sys
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
from proj import settings
settings.setting()
通过这种方式无论你在哪个目录,输入的命令是带了什么路径都能正常找到对应的模块。