在目录之间导入模块

Importing Module Between Directories

更新(显示最终代码)

因为这似乎很难解释,所以我分享了这个项目。对于那些提出这个问题的人,你可以在这里看到完整的项目:

https://github.com/jeffnyman/pacumen

调出对我来说有问题的文件:

graphical_pacman

https://github.com/jeffnyman/pacumen/blob/master/displays/graphical_pacman.py

布局

https://github.com/jeffnyman/pacumen/blob/master/mechanics/layout.py

有了 __init__.pysetup.py 文件,我现在可以 运行 这样的命令:

python displays/graphical_pacman.py

python mechanics/layout.py

执行这些命令时,所有导入现在都能正确解析。您可以看到我在每个文件中使用的所有导入语句以及各种 __init__.py 文件的位置。

原始问题

我无法完成看似简单的工作:在目录之间导入模块。这些都在 Python 3 中,所以如果可以的话,我不想到处都是 __init__.py 文件,这里的许多答案都建议使用 "right" 方式。

我有这样的结构:

project
  displays
    graphical_pacman.py
  mechanics
    layout.py

layout.py 文件有一个名为 get_layout() 的顶级函数,我想从 graphical_pacman.py.

调用它

转到所需的最少代码,在 graphical_pacman.py 我有:

import layout

if __name__ == '__main__':
  board = layout.get_layout("test_maze.lay")

在 IDE 中显示良好,甚至自动完成它。 运行 graphical_pacman.py 让我得到这个:

File "displays/graphical_pacman.py", line 3, in <module>
  import layout
ModuleNotFoundError: No module named 'layout'

然后我试了这个:

from mechanics.layout import get_layout

if __name__ == '__main__':
  board = mechanics.layout.get_layout("test_maze.lay")

也做不到:

File "displays/graphical_pacman.py", line 3, in <module>
  from mechanics.layout import get_layout
ModuleNotFoundError: No module named 'mechanics'

我试过这个:

from mechanics import layout

if __name__ == '__main__':
  board = layout.get_layout("test_maze.lay")

我试过这个:

from layout import get_layout

if __name__ == '__main__':
  board = get_layout("test_maze.lay")

无效。明白了:

File "displays/graphical_pacman.py", line 3, in <module>
  from layout import get_layout
ModuleNotFoundError: No module named 'layout'

我尝试使用相对导入(在事物前面加上 .),但这也不起作用。我也刚刚尝试使用 * 进行导入(本质上是导入所有内容)。也不起作用。当我说 "doesn't work" 时,我得到了上述错误的一些变体。

我已经从 displays 目录和根 project 目录中尝试了所有这些 运行ning 命令 python graphical_pacman.py。每次都出现同样的错误。

我也试过使用 sys.path,比如这个 sys.path.insert(0, '../mechanics')。我还根据我在这里看到的其他答案尝试了 sys.path.append('../') 的变体。同样,我得到的只是上述错误的变体。

我错过了什么?

第二次更新: 我已经发送了拉取请求,请查看。
这是我所做的:我从项目根目录创建了一个包,并使用 pip 安装了它。详情如下:

添加的文件:

pacumen
    __init__.py
    setup.py
    displays
        __init__.py
    library
        __init__.py
    mechanics
         __init__.py

pacumen/__init__.py 的内容:

from . import displays
from . import library
from . import mechanics

pacumen/setup.py 的内容:

import setuptools


setuptools.setup(
    name='pacumen',
    version='0.01dev',
    packages=['displays',
                'library',    
                'mechanics',
            ],
    author='jefferyman',
    author_email='something@something.com',
    description='Pacman. Duh.',
    long_description=open('README.md').read(),        
    )

pacumen/displays/__init__.py 的内容:

from . import graphical_helpers
from . import graphical_pacman
from . import graphical_support

pacumen/library/__init__.py 的内容:

from . import structures
from . import utilities

pacumen/mechanics/__init__.py 的内容:

from . import grid
from . import layout

文件变化:

pacumen/mechanics/layout.py:

from mechanics.grid import Grid

确保您的虚拟环境处于活动状态。 (进一步说明)。
最后,导航到项目根目录并将项目根目录安装为包:

pip install --editable .  # or pip install -e .  (Note the  period at the end)

现在只要激活了虚拟环境,导入应该就没有问题了。请确保使用样式的导入语句:

from mechanics.grid import Grid

虚拟环境创建和激活:

对于阅读者来说,现在是创建虚拟环境的好时机(如果尚未创建的话)。如果确保激活它。如果没有,导航到项目目录的根目录 (pacumen) 和 运行

$ python -m venv venvdir

然后,一旦创建,运行:

<project-root>$ .venvdir\Scripts\activate  # for windows.
OR
<project-root>$ source venvdir/bin/activate  # for linux bash

更新:首先要检查的是没有循环导入,这意味着您正在从 graphical_pacman 中导入一些来自 mechanics 的东西,以及一些来自 graphical_pacman 的 mechanics 中的其他东西。还要确保您的模块名称不与内置 python 模块的名称冲突。如果在这些方面都很好,

  1. 你试过吗?
    from project.mechanics import layout
  2. 如果这不起作用,请将 __init__.py 文件放入项目、显示、机制中。
    在 project/displays/__init__.py 添加 from . import graphical_pacman.
    在 project/mechanics/__init__.py 添加 from. import layout.
  3. 如果这些都不起作用,请从中创建一个包,导航到包根目录并使用 pip install -e .
  4. 在您的环境中安装它

我有一些有用的东西,所以我有 的答案,但完整的答案是我正在做的事情在 Python 中无法完成,到目前为止据我所知。这里的部分问题是,随着我了解更多,我意识到我的问题在我希望事情如何执行方面措辞不够好。

下面是可以工作的方式。如果在 graphical_pacman.py,我这样做:

if __name__ == '__main__':
  sys.path.append('../')
  from mechanics import layout

  board = layout.get_layout("test_maze.lay")

终于成功了!

但是...

我必须从 displays 目录而不是项目根目录中 运行 python graphical_pacman.py。 (意思是,我无法从项目根目录执行此命令:python displays\graphical_pacman.py。)

但是...

执行仍然 运行s 出错:

Traceback (most recent call last):
  File "graphical_pacman.py", line 18, in <module>
   from mechanics import layout
  File "../mechanics/layout.py", line 4, in <module>
   from grid import Grid
ModuleNotFoundError: No module named 'grid'

这是来自 layout.py 中的这一行:

from grid import Grid

如果我将该行更改为相对导入:

from .grid import Grid

现在displays 目录中的 运行 时,我的 python graphical_pacman.py 命令有效。

但是...

现在我不能只 运行 python layout.py,这也需要成为可能。这显然是因为相对导入现在在那里,所以我得到这个错误:

Traceback (most recent call last):
  File "layout.py", line 4, in <module>
   from .grid import Grid
ModuleNotFoundError: No module named '__main__.grid'; '__main__' is not a package

我发现的问题是我有时需要单独 运行 文件。您会看到 mechanics\layout.py 有一个 if __name__ == '__main__': 部分。 displays\graphical_support.py 也是如此。这些是必要的,这样人们就可以通过 运行 直接查看这些文件来了解工作原理。但最终这些将由目录根目录中的文件 used/imported 编排所有内容。有时文件(如布局)将依赖于其他文件(如网格)。

所以看起来答案是Python做不到我想做的,这基本上只是寻求灵活性。

我想我可以通过将所有文件放在一个目录中来处理所有这些问题,这似乎是这里的最佳答案,而且其他项目似乎也是这样做的。这似乎是一个糟糕的方法,但我正在权衡与导入机制的斗争,特别是因为我现在发现该机制根据执行以不同的方式工作。