对自定义包的引用
需求驱动学习。
前言
这篇文章是包和单元测试的姊妹篇,内容是如何在自己的工程中导入自定义包,而不出现导入错误。
在文章:包和单元测试中,已经叙述了如何单元测试的导入问题,本质上讲,只要导入的模块在搜索路径中,python就可以发现该模块。也验证了python
命令会将运行文件所在目录加到sys.path
中,而python -m unittest
命令,将运行命令所在目录加入到sys.path
中。
实验
为了写笔记,该系列实验仍然在Windows上进行,使用Python 2.7。
1. 同级目录引用自定义包
目录结构
如下:
1 | my_project |
my_project
是项目目录foo
是包目录tests
是对包的单元测试reference_foo_bar.py
是与包目录同级的工程文件,即同在my_project
下。
各文件内容
两个
__init__.py
文件、test_foo.py
都为空bar.py
内容:
1 | def dumb_true(): |
test_bar.py
内容如下,但今天的实验中不会用到单元测试。
1 | import unittest |
reference_foo_bar.py
内容如下:
1 | from foo import bar |
运行测试
测试命令如下:
1 | cd new_project |
测试结果如下:
1 | Hi, we can import foo and use it. |
太棒了,这是一个好的征兆,我们成功引用了模块foo.bar
下的dumb_true
函数,如果不明白原理,请看姊妹篇文章:包和单元测试
2. 不同目录引用自定义包
我们使用的标准库和第三方库,都是这种情况,因为这些包都不在我们工程的目录下。本质上讲,他们也都是自定义的,只不过在安装他们的时候,将他们所在的目录,放到了Python的搜索路径中,即sys.path
。
我们本实验中自定义的包,指我们自己写的工具包,这样我们可以在自己项目中的各处都可以使用。
目录结构
本实验目录结构如下,
1 | my_project |
建立新目录sub_project
,并将reference_foo_bar.py
移至此目录。
运行测试
1 | cd my_project |
得错误信息:找不到模块foo
1 | Traceback (most recent call last): |
问题来了
当前的搜索路径中包含...\sub_project
,在本目录下是找不到foo
的。
怎样才能让Python搜索到,我们自定义的包foo
呢,
方案1:安装我们自定义的包
模仿我们安装的标准库,与第三方的包,我们可以为foo
写一个setup.py
,然后安装它,这样Python永远都能找到它,任何工程也都能导入它,但是我们的包不完善,需要经常修改,并且我们这个包,也仅仅适用于我们当前的工程,所以这并不是一个理想的选择。
打包的教程在此:有兴趣者,请戳。
方案2:在每个文件中,修改sys.path
在每个文件中,都将foo
所在的目录的绝对路径添加到sys.path
。
1 | import sys |
但这样也存在明显的缺陷,丑陋而繁琐。
方案3:使用相对导入
这是一个馊主意。
相对导入只在包下才能工作,所以把my_project
变成包,然后使用相对导入。
在py_project
下加入__init__.py
,
修改reference_foo_bar .py
的内容为:
1 | from ..foo import bar |
运行相对导入要掌握正确的姿势,不然,蛋碎。
在new_project的父目录运行:
1 | python -m new_project.sub_project.reference_foo_bar |
运行成功。。。但这是一个馊主意,我们总不能把我们所有的项目都搞成包吧。包可以是项目,但项目不是包。
所以,放弃该方法。