【问题标题】:How does one handle multiple modules/packages in python?如何在 python 中处理多个模块/包?
【发布时间】:2021-04-21 15:42:08
【问题描述】:

我花了几个小时研究这个问题,但我仍然感到困惑。请发现我的无知很迷人。

我正在构建一个 python 程序,它可以让我在战舰游戏中让两个 AI 相互对抗。

这是我的目录结构:

.
├── ais_play_battleship
│   ├── game.py
│   ├── __init__.py
│   ├── player.py
│   └── ship.py
├── LICENSE
├── README.md
└── tests
    └── ship_test.py

2 directories, 7 files

目前,我正在尝试编写ship_test.py,但我似乎无法导入ais_play_battleship.ship。我得到了可怕的“ModuleNotFoundError”

以下是我的研究告诉我的问题:

  • 如果您想从另一个目录导入 python 代码,您应该将该目录设为包而不是模块。因此,我在ais_play_battleship 的根目录下放置了一个__init__.py 文件。
  • Python 只会搜索启动 python 的目录以及您正在运行的脚本的目录。因此,我一直在尝试通过从根目录运行 python3 tests/ship_tests.py 来启动我的测试。

这是我的具体问题:

  • 为什么错误是“ModuleNotFound”错误?不应该是“PackageNotFound”吗?
  • 将 ais_play_battleship 制作成一个包是否正确?
  • 如何将我的测试保存在单独的目录中,并且仍然使用 ais_play_battleship 中的代码?

请原谅我,因为我不太擅长在 StackOverflow 上提问。请告诉我如何改进。

【问题讨论】:

  • 我怀疑您忽略了将path 设置为包含ais_play_battleship
  • 您是否建议我执行以下操作? import sys sys.path.insert(0, '/path/to/application/app/folder') 另外,我认为如果我从父目录运行程序,我不需要。这是错的吗?

标签: python import module package


【解决方案1】:

我正在回答我自己的问题,因为我还没有得到满意的答案。我找到的最好的资源是available here。总结:

Python 不会在您运行 python 的目录中搜索模块。此外,添加__init__.py 文件以使目录成为包不足以使其对在另一个文件夹中运行的python 实例可见。您还必须安装该软件包。因此,访问另一个目录中的模块的唯一两种方法是:

  1. 在站点包中安装打包的模块(这需要创建一个setup.py 文件并使用pip install . 安装更多信息是available here.
  2. 修改路径以解析模块

我最终选择了第二个选项,原因如下所述。

第一个选项要求在每次更改软件包时重新安装软件包。这在不断变化的代码库上很困难,但可以通过使用构建自动化来简化。不过,我想避免这种额外的复杂性。

我很长一段时间都避免使用第二个选项,因为修改路径似乎需要对我的模块的绝对路径进行硬编码,这显然是不可接受的,因为每个开发人员都必须编辑该路径以适应他们的环境。但是,this guide 提供了解决此问题的方法。创建一个./tests/context.py 文件,内容如下:

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

然后,在我的 ship_test.py 模块中,我导入了所需的上下文和模块:

import context
import ais_play_battleship.ship
# (I include the submodule ship because ais_play_battleship itself does not have
# any attributes or methods, and the submodule ship is the only one I am testing
# in ship_test.py)

这更适合我的项目,因为它可以按预期工作,而不必担心安装我的包(或安装我的包的方法)。

【讨论】:

  • re:"The first option requires one to reinstall the package at every change to a package. This is difficult on a constantly-changing codebase, but can be made easier by using build automation. However, I'd like to avoid this added complexity." 对于开发,您可以执行所谓的“可编辑”安装:pip install -e .
【解决方案2】:

要解决此问题而不依赖于破解您的 sys.path,请创建一个 setup.py 文件并作为测试运行器的构建步骤,首先运行 pip install .。您可能需要使用tox 之类的工具。

在顶级目录中:

setup.py

from setuptools import setup

setup(name='ais_play_battleship')

tox.ini

[tox]
envlist = py36, py37

[testenv]
deps=pytest
commands=
    pip install . --quiet
py.test -q

然后运行您的测试(在本例中,我们使用 tox 来执行此操作,以便我们还可以配置测试环境的配置方式):tox

【讨论】:

  • 感谢您的帮助。在这一点上我不会担心 tox,我只关心理解导入、模块和包。 setup.py 有什么作用,我为什么需要它? pip install . 是做什么的?
  • 如果你想要一个全面的答案,我建议你阅读这个:python-packaging.readthedocs.io/en/latest 否则,它的要点是pip 只是一个 python 包管理器,setup.py 是一个魔法文件pip python 包管理器在它想要安装一个作者想要捆绑在一起作为单个“python 包”的代码目录时寻找它。您可以将其视为如何从某个隐式项目根目录声明并让模块可用于您的 python PATH 的一组文件和子目录的配置。
  • 我阅读了您链接到的页面,这对解决我的问题很有帮助。谢谢!
  • 好吧,我说得太早了。我仍然有问题。我发布了自己的答案,因为我认为我已经弄清楚了,但我错了。然后,我尝试探索毒物,但我也没有得到它。目前我正在处理的错误报告no module named ais_play_battleship。这是the full ouput of tox.
【解决方案3】:

tests/ship_test.py 作为模块运行

python -m tests.ship_test

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-04-04
    • 2017-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-09
    相关资源
    最近更新 更多