带有doctest的Python项目中的导入

4 投票
3 回答
1540 浏览
提问于 2025-04-15 19:57

我有一个Python项目,目录结构如下:

/(some files)
/model/(python files)
/tools/(more python files)
...

在这个项目里,我有几个子目录,每个目录里都有Python文件,并且这些目录之间有一些依赖关系,比如工具会被模型使用等等。现在我的问题是,我想为modelstools都写一些文档测试,并且希望能通过命令行像这样运行测试:./model/car.py。我能做到这一点,但需要写很多繁琐的代码。我想知道有没有正确的方法,或者说有没有什么好的解决方案?

问题:我应该怎么写我的导入语句?

谢谢!这里有个例子...

tools/tool.py的内容:

#!/usr/bin/env python
"""
   >>> is_four(21)
   False
   >>> is_four(4)
   True
"""

def is_four(val):
    return val == 4

if __name__ == '__main__':
    import doctest
    doctest.testmod()

... 还有model/car.py

#!/usr/bin/env python   
"""
   >>> car = Car()
   >>> car.ok()
   True
"""

from tools.tool import *

class Car(object):
    def __init__(self):
        self.tire_count = 4
    def ok(self):
        return is_four(self.tire_count)

if __name__ == '__main__':
    import doctest
    doctest.testmod()

我在car.py的开头加了以下几行代码,这样可以运行,但看起来不太好。 :(

if __name__ == '__main__':
    import sys
    import os
    sys.path.append(os.path.abspath(os.path.dirname('..')))

3 个回答

0

不要以这种方式去修改 sys.path。相反,你可以在运行 python 的时候使用 $PYTHONPATH 来指定基础目录,或者直接在那个目录下使用 python -m model.car 来运行。

1

使用包。你需要在你的工作目录和所有子文件夹里添加一个 __init__.py 文件,这样当你导入模块时,如果当前目录找不到这个模块,它会去上级目录里找。

可以参考这个链接了解更多信息:http://www.network-theory.co.uk/docs/pytut/Packages.html

另外,这个问题和以下内容是重复的:

从相对路径导入模块

2

你想做的是相对导入。在Python中,这个功能是可以正常工作的,但它是基于模块的层级,而不是文件系统的层级。我知道,这听起来有点复杂。

这意味着,如果你在一个子目录中运行脚本,它看不到上层目录,因为对于正在运行的脚本来说,模块的根目录就是当前目录:没有上层模块。

那么,相对导入有什么用呢?

其实,子目录中的模块可以导入上层目录中的模块,只要这些模块本身是从上层目录导入的。

在你的情况下,这意味着你必须从根目录“/”来运行你的脚本,这样它就成为模块的根目录,子模块就可以使用相对导入。

解决你问题的一个方法是去掉你的 if __name__ == "__main__" 这部分代码,然后创建一个 /tests.py 文件:

import doctest
from model import car
from tools import tool

doctest.testmod(car)
doctest.testmod(tool)

然后在这里运行所有的测试。

最终,你会想要自动化这个过程,一个简单的解决方案是使用 unittest,这样你可以创建测试套件,只需添加你想测试的模块名称:

import unittest
import doctest

modules = ("model.car", 
           "tools.tool")

suite = unittest.TestSuite()
for mod in modules:
    suite.addTest(doctest.DocTestSuite(mod))
runner = unittest.TextTestRunner()
runner.run(suite)

另一个推荐的解决方案是使用像 nose 这样的工具,它可以为你自动化这个过程。

easy_install nose
nosetests --with-doctest # done :-)

顺便说一下,尽量避免使用 from x import *。这在快速编写脚本时可以用,但当你的程序变得复杂时,你真的需要明确地指定你要导入的内容。可以用 import x 或者 from x import y

撰写回答