Python 3:从父目录中的Python文件导入函数

5

我是单元测试的新手,正在尝试在Python 3中创建我的第一个单元测试文件。因此,我只是模仿在线课程,有以下非常基本的文件夹结构。

D:\Projects\Projects\unit_testing_course  # parent dir absolute path

unit_testing_course/                      # root/parent folder
    first_project.py                      # has a function called avg() 
    tests/                                
        test_first_project.py             # I want to import avg() into this file

test_first_project.py是我用于单元测试的文件,我想从位于它上层父目录中的first_project.py中导入avg()函数。

我使用Windows 10,在路径“/d/Projects/Projects/unit_testing_course”下打开git bash(即包含first_project.py的父目录,我要将该模块/函数导入到tests子目录中的test_first_project.py文件中)。我一直在运行python tests/test_first_project.py

我很快发现我不能使用像'from .. import first_project'或'from .. import avg'这样的语句,因为会导致“ValueError: attempted relative import beyond top-level package”的错误消息。

因此,从阅读文档中我得知,'from'和'import'都是指令,旨在与模块或包一起使用。 https://docs.python.org/3/tutorial/modules.html

“Python有一种在文件中定义并在脚本或交互式解释器实例中使用它们的方法。这样的文件称为模块。来自模块的定义可以导入到其他模块或主模块(在执行顶部和计算器模式的脚本中可以访问变量的集合)中。” “模块是一个包含Python定义和语句的文件,文件名是附加了.py后缀的模块名。在模块内,模块的名称(作为字符串)可用作全局变量__name__的值。” “因此,就Python 3而言,似乎每个.py文件都是一个模块,模块名基本上是没有 .py扩展名的 .py文件名。似乎我正在尝试从test_first_project.py文件的父目录导入一个模块。” “该文档链接似乎有助于解释以下情况:您在执行程序/ Python shell的当前目录中有一个模块,并且您只是想将该模块中的函数导入到该程序中。”
但在这种情况下,似乎我们需要告诉Python在特定目录中查找名为“first_project”的模块,即test_first_project.py文件的父目录。我查看了模块搜索路径部分,https://docs.python.org/3/tutorial/modules.html#the-module-search-path

当导入名为spam的模块时,解释器首先搜索具有该名称的内置模块。如果未找到,则在变量sys.path给出的目录列表中搜索名为spam.py的文件。 初始化后,Python程序可以修改sys.path。运行脚本的目录被放置在搜索路径的开头,位于标准库路径之前。

然后我尝试...
import sys
import os
p_dir_path = os.path.abspath('../')
sys.path.append(p_dir_path)
from  first_project import avg

我仍然收到相同的错误信息:“ValueError:尝试超出顶级包的相对导入”。我希望通过程序修改sys.path可以改变这种情况...

那么,我实际上需要做什么才能从父目录导入模块呢? 或者...在Python中不可能这样做吗?我需要使用包而不是模块吗(即我需要在父目录中创建一个init.py)?

我看过stackoverflow上关于尝试从不同目录导入文件和其他情况的其他问题和帖子,但是模仿这些问题上的语法并没有帮助我,我认为我已经花费了太多时间在这个问题上。我也确定我不是唯一想创建单元测试并因导入语句、模块和包的问题而分心的人。


从父目录运行它:python tests/test_first_project.py 或者简单地使用 python -m unittest。可能需要一个 tests/__init__.py 文件。 - Klaus D.
有趣的是,我已经在做这件事了。我已经打开了git bash,并定位到/d/Projects/Projects/unit_testing_course目录,我继续使用'python tests/test_first_project.py',但是我遇到了这些错误。 - Byron Smith
好的,你在展示非常少的代码的同时写了很多文本。 - Klaus D.
2
我在父目录(unit_testing_course)和子目录(unit_testing_course/tests/)中都有__init__.py。使用from first_project import avg仍然失败,并显示“ModuleNotFoundError: No module named 'first_project'”消息。是的,我知道这里有很多文本,我可能正在过度复杂化此问题。但我对Python包和模块感到非常困惑,即使对于尝试从不同目录中获取函数的相对简单的任务也是如此。由于这个问题,我无法通过我真正包含的5行代码。 - Byron Smith
1个回答

2
你需要为你的项目适当设置PYTHONPATH变量。在unit_testing_course中运行。"最初的回答"
export PYTHONPATH=$(pwd)

在你的shell中执行以下代码,并考虑下面的代码: first_project.py 将"Original Answer"翻译成"最初的回答"
def avg(l):
    return sum(l)/len(l) if len(l) > 0 else 0

tests/test_first_project.py

import unittest 
from first_project import avg

class Test(unittest.TestCase):
    ...

tests/__init__.py

from .test_first_project import *

运行 python -m unittest tests 命令即可实现预期效果(假设 python 指向所需的 Python 版本)。您还可以使用 virtualenv 来修改其 venv/bin/activate 文件,以自动执行此路径设置。当您的项目继续增长时,我建议在基础目录中拥有两个单独的目录 - 一个用于项目本身,另一个用于测试。然后任意使用每个目录中的相对导入。这将使您的项目具有灵活性,因为您可以选择将其作为软件包子模块等使用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接