我试着通读关于兄弟姐妹导入的问题,甚至 软件包文档,但我还没找到答案。

结构如下:

├── 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

示例和测试目录中的脚本如何从 API模块和从命令行运行?

另外,我希望避免对每个文件都使用难看的sys.path.insert。肯定 这可以在Python中完成,对吧?


当前回答

对于主要问题:

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

从. .进口siblingfolder

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

从. .兄弟文件夹导入a_file

在兄弟文件夹中的文件中调用a_function作为模块:

从. . siblingmodule。A_file import func_name_exists_in_a_file

最简单的方法。

进入lib/site-packages文件夹。

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

如果不存在,就把它变成一个…把你想要的文件夹放在那里

添加之后…, python会自动将该文件夹视为类似site-packages的文件夹,您可以将该文件夹或子文件夹中的每个脚本作为模块调用。

这是我用手机写的,很难设置成让每个人都能舒服地阅读。

其他回答

我还没有对python学有必要的理解,以了解在不相关的项目之间共享代码的预期方式,而没有兄弟姐妹/亲戚导入黑客。在那一天到来之前,这就是我的解决方案。用于从..导入内容的示例或测试。\api,它看起来像:

import sys.path
import os.path
# Import from sibling directory ..\api
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/..")
import api.api
import api.api_key

下面是我在tests文件夹的Python文件顶部插入的另一个替代方案:

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

七年之后

由于我把答案写在下面,修改sys.;Path仍然是一种快速而肮脏的技巧,适用于私有脚本,但是已经有了一些改进

安装包(在virtualenv中或不在virtualenv中)会给你想要的,尽管我建议使用pip来完成,而不是直接使用setuptools(并使用setup.cfg存储元数据) 使用-m标志并作为包运行也可以(但是如果您想将工作目录转换为可安装包,则会有点尴尬)。 具体来说,对于测试,pytest能够在这种情况下找到api包,并负责sys. exe。给你的路径技巧

所以这取决于你想做什么。但是,在您的情况下,由于您的目标似乎是在某种程度上制作一个适当的包,因此通过pip -e安装可能是您的最佳选择,即使它还不完美。

旧的答案

正如在其他地方已经说过的,可怕的事实是,你必须做一些丑陋的hack来允许从__main__模块的兄弟模块或父包导入。该问题在PEP 366中有详细说明。PEP 3122试图以更合理的方式处理导入,但Guido拒绝了它

唯一的用例似乎是运行发生的脚本 在一个模块的目录中,我总是把它看作一个 反模式。

(在这里)

不过,我经常使用这种模式

# Ugly hack to allow absolute import from the root folder
# whatever its name is. Please forgive the heresy.
if __name__ == "__main__" and __package__ is None:
    from sys import path
    from os.path import dirname as dir

    path.append(dir(path[0]))
    __package__ = "examples"

import api

这里的路径[0]是运行脚本的父文件夹,目录(路径[0])是顶级文件夹。

虽然我还不能使用相对导入,但它确实允许从顶层(在示例api的父文件夹中)进行绝对导入。

对于2021年的读者:如果你对pip没有信心,请安装-e:

考虑一下这个层次结构,正如Python 3中相对导入的答案所推荐的那样:

MyProject
├── src
│   ├── bot
│   │   ├── __init__.py
│   │   ├── main.py
│   │   └── sib1.py
│   └── mod
│       ├── __init__.py
│       └── module1.py
└── main.py

main.py的内容,这是起点,我们在这里使用绝对导入(没有前导点):

from src.bot import main


if __name__ == '__main__':
    main.magic_tricks()

bot/main.py的内容,它利用了显式的相对导入:

from .sib1 import my_drink                # Both are explicit-relative-imports.
from ..mod.module1 import relative_magic

def magic_tricks():
    # Using sub-magic
    relative_magic(in=["newbie", "pain"], advice="cheer_up")
    
    my_drink()
    # Do your work
    ...

原因如下:

当执行python MyProject/main.py时,/到/MyProject的路径被添加到sys.path中。 绝对导入import src。机器人会读的。 从…mod部分意味着它将上升一级到MyProject/src。 我们能看看吗?是的,因为路径/to/MyProject被添加到sys.path中。

所以重点是:

我们应该把主脚本放在MyProject/src旁边,因为当做相对引用时,我们不会离开src,绝对导入导入src。为我们提供合适的作用域:src/ scope。

参见:ModuleNotFoundError:没有名为“sib1”的模块

存在的问题:

你只是不能让import mypackage在test.py中工作。你将需要一个可编辑的安装,改变到路径,或改变__name__和路径

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中导入之前,请将上述内容放在上面。这是它。你现在可以导入mypackage了。

这在Windows和Linux上都可以工作。它也不会关心从哪个路径运行脚本。它足够短,可以拍打任何你需要它的地方。

为什么有效:

The sys.path contains the places, in order, where to look for packages when attempting imports if they are not found in installed site packages. When you run test.py the first item in sys.path will be something like /mnt/c/Users/username/Desktop/demo/dev i.e.: where you ran your file. The oneliner will simply add the sibling folder to path and everything works. You will not have to worry about Windows vs Linux file paths since we are only editing the last folder name and nothing else. If you project structure is already set in stone for your repository we can also reasonably just use the magic number 3 to slice away dev and substitute src