我试着通读关于兄弟姐妹导入的问题,甚至
软件包文档,但我还没找到答案。
结构如下:
├── 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的文件夹,您可以将该文件夹或子文件夹中的每个脚本作为模块调用。
这是我用手机写的,很难设置成让每个人都能舒服地阅读。
七年之后
由于我把答案写在下面,修改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的父文件夹中)进行绝对导入。
你不需要也不应该侵入系统。路径,除非它是必要的,在这种情况下,它不是。使用:
import api.api_key # in tests, examples
从项目目录运行:python -m tests.test_one。
你可能应该把测试(如果它们是api的单元测试)移动到api内部并运行python -m api。测试运行所有测试(假设有__main__.py)或python -m api.test。Test_one来运行Test_one。
你也可以从示例中删除__init__.py(它不是Python包),并在安装api的virtualenv中运行示例,例如pip install -e。在virtualenv中,如果你有正确的setup.py,就会安装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”的模块