【问题标题】:python resolve module name conflict when imported module name is not well defined当导入的模块名称未明确定义时,python解决模块名称冲突
【发布时间】:2020-10-12 10:16:51
【问题描述】:

我搜索了很多但找不到答案,我尝试了诸如相对导入、使用别名(在当前项目或导入的模块 init 文件中)等方法。 为了帮助轻松重现它,我检查了github 中的演示代码。

repo 有两个文件夹,AB。它们被视为由不同开发人员创建的不同项目。我将它们放入一个单独的 git repo 中,因为这对您来说更容易演示目的

文件列表:

.
├── A
│   ├── init.py
│   ├── foo_a.py
│   └── utils_module
│   ├── init.py
│   └── utils.py
├── B
│   ├── init.py
│   ├── main.py
│   └── utils_module
│   ├── init.py
│   └── utils.py
└── README.md

A 和 B 都有它的主入口,并且可以成功运行(只需引用它的 utils,然后回显 A 或 B)。

现在的问题是,A 有一些功能(foo_a)可以(但不是设计为)被 B 重用,因此 B 需要导入它。但我不想将这两个项目合并为一个,因为它们是分开开发的。并且重构 A 的文件夹/包结构也不在这个问题的范围内(除了在 init 中添加一些代码,如果有帮助的话)。你问我为什么,我只是在学习python的导入机制。

为了能够在 B 中导入 A,我使用 sys.path.append(A's relative or absolute path)。但是在运行B时,它给出了错误:

ImportError: 无法导入名称“echo_a”。

这个错误是合理的,因为 A 和 B 都有同一个模块,名为 utils_module/utils(我也不想更改),并且 B 中的 utils 似乎覆盖了 A。

如果在 A 中添加 __init__ 文件,并在 B.main.py 中添加导入前缀 A. 并在 A.foo_a.py 中使用相对导入(from .),则运行 B 可以获得正确的输出。但是这次A不能像以前那样运行,报错

ModuleNotFoundError: 没有名为 'ma​​in.utils_module' 的模块; 'ma​​in' 不是包

那么如何通过更改导入语法来解决这个问题?谢谢!

【问题讨论】:

  • 一种方法是将其拆分为 3 个项目,并为两者制作第三个“通用”模块,其中包含 foo_a
  • 谢谢,你的方法有点好。但这增加了依赖。 A 和 B 目前可以自己动手,我喜欢。
  • 你能澄清一下你在挣扎什么吗?如图所示,这些模块实际上是A.utilsB.utils(同样是A.foo_aB.main),所以没有冲突。 “包”实际上是否设计为完全*的?如果是这样,在上游修复它们是最合理的做法。
  • @MisterMiyagi 在 B 中使用 sys.path.append() 时,utils 模块的导入将只是 import utils(或 from utils import echo_a),这与它自己的 utils 冲突。当前目录和 B 的目录都有一个名为 utils 的*模块
  • @MisterMiyagi 我不太理解你的问题。这两个项目是分开设计的,它们被设计成不被其他代码导入。所以没有包名(或者也许你的 top-level 包意味着同样的事情?)。如果我创建了一个永远不会被其他项目使用的项目,那么为我定义一个包名似乎毫无意义。而且我并不是要重构项目结构,因为我只是在询问可能性和语法,出于教育和学习的目的,最简单的解决方案是将 A 和 B 合并到一个通用模块中。但这不是这里的主题。

标签: python python-import project-management python-module


【解决方案1】:

您希望将 A 和 B 视为包 - 因此在每个文件夹(A 和 B)中,您将添加一个名为 __init__.py 的完全空文件。

然后在 B 中,您将使用类似的东西唯一标识 A 的 utils 模块

from A.utils import echo_a

当您刚刚将每个目录添加到路径中时,是的,utils 模块被解释器首先找到的那个覆盖。在包中结构化是模块化隔离代码的一种方法。

那么你运行它的方式是从A和B上面的目录,你运行类似python -m B.main的东西


如果事情真的完成了,您还可以为 A 和 B 分别建立一个 setup.py 并使其可以使用 pip 安装另一个。

【讨论】:

  • 谢谢。我创建了一个分支A-as-module 演示您的解决方案。问题是,我必须更改 A 的代码以反映包名称的更改,我必须在 foo_a.py 中使用from A.utils import echo_a,否则运行 B.main.py 会抛出异常。除了添加 init 文件之外,我不想更改 A 的代码。从项目 A 的角度来看,给整个包命名似乎没什么用。
  • 通常必须从你自己的包中导入东西,包括它自己的包名。这是对项目 A 包文件的一个很好的封装。您可以将导入更改为相对导入:from .utils import echo_A - 但是将 A 视为其他人的包而不是自己的包是很棘手的
  • 您的解决方案可以解决 B 导入 A,但现在 A 本身出现此错误:ModuleNotFoundError: No module named 'main.utils_module'; 'main' is not a package。请查看我的更新代码。