同级包导入

371

我曾尝试阅读有关兄弟导入的问题,甚至包括包文档,但我仍然没有找到答案。

使用以下结构:

├── LICENSE.md
├── README.md
├── api
│   ├── __init__.py
│   ├── api.py
│   └── api_key.py
├── examples
│   ├── __init__.py
│   ├── example_one.py
│   └── example_two.py
└── tests
│   ├── __init__.py
│   └── test_one.py

如何让 examplestests 目录下的脚本能够从 api 模块导入并且能够从命令行运行?

另外,我想避免为每个文件使用丑陋的 sys.path.insert hack。这在 Python 中肯定有更好的方法,对吗?


15
我建议跳过所有的sys.path黑科技,阅读到目前为止唯一实际的解决方案(在7年之后!)链接 - Aran-Fey
2
顺便说一下,还有另一个好的解决方案:将可执行代码与库代码分开;大多数情况下,包中的脚本一开始就不应该是可执行的。 - Aran-Fey
这非常有帮助,无论是问题还是答案。我只是好奇,在这种情况下,“被接受的答案”为什么不同于获得赏金的答案? - Indominus
1
@Aran-Fey 在这些相对导入错误的问答中,这是一个被低估的提醒。我一直在寻找一个解决方法,但内心深处我知道有一种简单的方式可以设计出这个问题。并不是说这是每个人都适用的解决方案,但它是一个很好的提醒,因为它可能适用于许多人。 - colorlace
19个回答

1

以防万一有人在Eclipse上使用Pydev,可以通过在项目->属性下的左侧菜单Pydev-PYTHONPATH中设置外部库来添加兄弟节点的父路径(从而是调用模块的父路径)作为外部库文件夹。然后您可以从兄弟节点导入,例如:from sibling import some_class


1
我想评论np8提供的解决方案,但是我的声望不够,所以我只能提到你可以按照他们建议的方式创建一个setup.py文件,然后从项目根目录执行pipenv install --dev -e .将其转换为可编辑依赖项。然后您的绝对引用将起作用,例如from api.api import foo,您无需处理系统范围的安装问题。 文档

1
如果您正在使用pytest,那么 pytest文档描述了如何从单独的测试包中引用源包的方法。
建议的项目目录结构为:
setup.py
src/
    mypkg/
        __init__.py
        app.py
        view.py
tests/
    __init__.py
    foo/
        __init__.py
        test_view.py
    bar/
        __init__.py
        test_view.py

setup.py文件的内容:

from setuptools import setup, find_packages

setup(name="PACKAGENAME", packages=find_packages())

可编辑模式下安装软件包:

pip install -e .

这篇pytest文章引用了Ionel Cristian Mărieș的博客文章


-1
在你的主文件中添加以下内容:

import sys
import os 
sys.path.append(os.path.abspath(os.path.join(__file__,mainScriptDepth)))

mainScriptDepth = 主文件相对于项目根目录的深度。

这是您的情况 mainScriptDepth = "../../"。然后,您可以通过从项目根目录指定路径(from api.api import *)进行导入。


-1

问题:

test.py中,您无法简单地使用import mypackage。您需要进行可编辑安装、更改path或更改__name__path

demo
├── dev
│   └── test.py
└── src
    └── mypackage
        ├── __init__.py
        └── module_of_mypackage.py

--------------------------------------------------------------
ValueError: attempted relative import beyond top-level package

解决方案:

import sys; sys.path += [sys.path[0][:-3]+"src"]

将上述内容放置在test.py的导入尝试之前。就是这样。现在你可以import mypackage
这将在Windows和Linux上都有效。它也不会在乎您从哪个路径运行脚本。它足够短,可以随时添加到任何可能需要它的地方。
为什么它有效: sys.path包含按顺序查找软件包的位置,如果它们未在已安装的站点包中找到,则尝试导入它们。当您运行test.py时,sys.path中的第一项将是/mnt/c/Users/username/Desktop/demo/dev之类的东西,即:您运行文件的位置。这个一句话代码将简单地将同级文件夹添加到路径中,一切都可以工作。您不必担心Windows与Linux文件路径,因为我们编辑最后一个文件夹名称而不涉及其他任何内容。如果您的项目结构已经在您的存储库中确定,我们也可以合理地使用魔数3来删除dev并替换src

-1

不想手动编辑sys.path,也不喜欢可编辑的安装方式?

pip install importmonkey

[github]
[pip]

示例结构:

您想从父路径/兄弟路径导入某些内容,但出现以下错误之一: ModuleNotFoundErrorImportErrorSystemError

├─ src
│   └─ project
│       ├─ __init__.py
│       └─ module.py
└─ test
    └─ test.py

解决方案:
# In test.py
# You can add as many paths as needed, absolute or relative, in any file.
# Relative paths start from the current __file__ directory.
# Normal unix path conventions work so you can use '..' and '.' and so on.
# The paths you try to add are checked for validity etc. help(add_path) for details.

from importmonkey import add_path
add_path("../src")
import project

披露关系:我是importmonkey的创始人。

-2

关于主要问题:

调用兄弟文件夹作为模块:

from .. import siblingfolder

从兄弟文件夹中调用a_file.py作为模块:

from ..siblingfolder import a_file

从兄弟文件夹中的文件中调用函数作为模块:

from..siblingmodule.a_file import func_name_exists_in_a_file

最简单的方法是:

进入lib/site-packages文件夹。

如果存在“easy_install.pth”文件,只需编辑它并添加您想要将其作为模块的脚本所在的目录。

如果不存在,请创建一个...并将您想要的文件夹放在那里。

添加后,Python会自动将该文件夹视为类似于site-packages,并且您可以从该文件夹或子文件夹调用每个脚本作为模块。

我是用手机写的,很难设置使每个人都能舒适地阅读。


-4
  1. 项目

1.1 用户

1.1.1 about.py

1.1.2 init.py

1.2 技术

1.2.1 info.py

1.1.2 init.py

现在,如果您想从技术包中的info.py模块访问用户包中的about.py模块,则必须将cmd(在Windows中)路径带到Project即**C:\Users\Personal\Desktop\Project>**,如上面的包示例所示。从此路径,您必须输入python -m Package_name.module_name。例如,对于上面的包,我们必须执行以下操作:

C:\Users\Personal\Desktop\Project>python -m Tech.info

重要提示

  1. info模块后不要使用.py扩展名,即python -m Tech.info。
  2. 输入此命令,其中兄弟包位于同一级别。
  3. -m是标志,您可以从cmd中键入python --help来检查它。

-4

首先,您应该避免使用与模块本身相同的文件名。这可能会破坏其他导入。

当您导入一个文件时,解释器首先检查当前目录,然后搜索全局目录。

examplestests中,您可以调用:

from ..api import api

我在使用Python 2.7.1时得到了以下错误:Traceback (most recent call last): File "example_one.py", line 3, in <module> from ..api import api ValueError: Attempted relative import in non-package - zachwill
2
哦,那你应该在顶级目录下添加一个 __init__.py 文件。否则 Python 就无法将其视为模块。 - user780363
9
无法实现,问题不在于父文件夹不是一个包,而是因为模块的__name____main__而不是package.module,Python无法看到它的父包,所以“.`”指向空。 - Evpok

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