python - 如何构建我的 Python 项目以允许从子目录导入命名模块

标签 python python-3.x organization

这是我的目录结构:

Projects
    + Project_1
    + Project_2
    - Project_3
        - Lib1
            __init__.py # empty
            moduleA.py
        - Tests
            __init__.py # empty
            foo_tests.py
            bar_tests.py
            setpath.py
        __init__.py     # empty
        foo.py
        bar.py

目标:

  1. 有一个有组织的项目结构
  2. 能够在必要时独立运行每个 .py 文件
  3. 能够引用/导入同胞和堂兄弟模块
  4. 将所有 import/from 语句放在每个文件的开头。

我通过使用上述结构获得了第一名

通过执行以下操作(按照 this excellent guide 的建议),我基本上达到了 2、3 和 4

在任何需要访问父模块或堂兄弟模块的包中(例如上面的 Tests 目录),我包含一个名为 setpath.py 的文件,其中包含以下代码:

import os
import sys
sys.path.insert(0, os.path.abspath('..'))

sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.abspath('...'))

然后,在每个需要父/表亲访问的模块中,例如 foo_tests.py,我可以像这样编写一个干净整洁的导入列表:

import setpath      # Annoyingly, PyCharm warns me that this is an unused import statement
import foo.py

在 setpath.py 中,第二个和第三个插入对于此示例而言并非绝对必要,但作为故障排除步骤包括在内。

我的问题是这只适用于直接引用模块名称的导入,而不适用于引用包的导入。例如,在 bar_tests.py 中,当 直接 运行 bar_tests.py 时,下面的两个语句都不起作用。

import setpath

import Project_3.foo.py  # Error
from Project_3 import foo  # Error

我收到错误“ImportError:没有名为‘Project_3’的模块”。

奇怪的是,我可以直接从 PyCharm 中运行该文件,而且运行良好。我知道 PyCharm 正在使用 Python Path 变量在幕后施展魔法以使一切正常,但我无法弄清楚它是什么。由于 PyCharm 只是运行 python.exe 并设置一些环境变量,因此应该可以从 Python 脚本本身中克隆此行为。

由于与这个问题不太相关的原因,我必须使用 Project_3 限定符来引用 bar

我对任何能够实现上述目标同时仍能满足我早先目标的解决方案持开放态度。如果有更好的目录结构,我也愿意使用另一种目录结构。我读过 Python doc on imports和包裹,但我仍然不知所措。我认为一种可能的方法是手动设置 __path__ 变量,但我不确定需要更改哪个变量或将其设置为什么。

最佳答案

这些类型的问题符合“主要基于意见”的标准,所以让我分享一下我的意见,我会怎么做。

首先“能够在必要时独立运行每个 .py 文件”:要么该文件是一个模块,因此不应该直接调用它,要么它是独立的可执行文件,那么它应该从顶层开始导入它的依赖项(您可以通过使用 setup.py entry_points 在代码中避免它或者更确切地说将它移动到公共(public)位置,但是您以前的可执行文件有效地转换为模块)。是的,这是导致误解的 Python 模块模型的弱点之一。

其次,使用 virtualenv(或 Python3 中的 venv)并将每个 Project_x 放入单独的一个。这样项目的名称就不会成为 Python 模块路径的一部分。

第三,您提供的链接提到了 setup.py——您可以使用它。将您的自定义代码放入 Project_x/src/mylib1,创建 src/mylib1/setup.py,最后将您的模块放入 src/mylib1/mylib1/module.py。然后你可以像安装任何其他包一样通过 pip 安装你的代码(或者 pip -e 所以你可以直接处理代码而无需重新安装它,尽管不幸的是它有一些限制)。

最后,正如您已经在评论中确认的那样 ;)。您当前模型的问题是,在 sys.path.insert(0, os.path.abspath('...')) 中,您错误地使用了 Python 模块的表示法,这对系统来说是不正确的路径并应替换为 '../..' 以按预期工作。

关于python - 如何构建我的 Python 项目以允许从子目录导入命名模块,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35738408/

相关文章:

python - Pygame 将 28x28 像素缩放到 420x420

python-3.x - 如何增加 ipywidgets 中的 slider 长度

c++ - 具有许多第三方依赖项的大型跨平台 C++ 项目在磁盘上的物理布局

python - Python 装饰器的参数

python - 我的 Python 中 Tic Tac Toe 的极小极大算法显示了最大递归误差

python - 类型错误 : 'int' object is not iterable?

python - 在 Python 中按数字阈值排序的有效方法?

tdd - 如何从 TDD 的角度来对待 future 的需求

github - GitHub 项目的流量图是否包括页面流量?

python - 正确的循环从txt文件读取数据、比较数据和写入数据