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

结构如下:

├── 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中完成,对吧?


当前回答

七年之后

由于我把答案写在下面,修改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的父文件夹中)进行绝对导入。

其他回答

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

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

对于主要问题:

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

从. .进口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.append -hacks,但我发现了另一种解决问题的方法。

总结

将代码打包到一个文件夹中(例如packaged_stuff) 创建setup.py脚本,其中使用setuptools.setup()。(参见下面的minimal setup.py) 使用Pip install -e <myproject_folder>将包安装到可编辑状态 从packaged_stuff导入using。Modulename import function_name


设置

起点是您提供的文件结构,包装在名为myproject的文件夹中。

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

我会打电话给。在我的例子中,它位于C:\tmp\test_imports\。

api.py

作为测试用例,让我们使用下面的./api/api.py

def function_from_api():
    return 'I am the return value from api.api!'

test_one.py

from api.api import function_from_api

def test_function():
    print(function_from_api())

if __name__ == '__main__':
    test_function()

尝试运行test_one:

PS C:\tmp\test_imports> python .\myproject\tests\test_one.py
Traceback (most recent call last):
  File ".\myproject\tests\test_one.py", line 1, in <module>
    from api.api import function_from_api
ModuleNotFoundError: No module named 'api'

尝试相对导入也不会起作用:

使用from ..api。API导入函数from_api的结果为

PS C:\tmp\test_imports> python .\myproject\tests\test_one.py
Traceback (most recent call last):
  File ".\tests\test_one.py", line 1, in <module>
    from ..api.api import function_from_api
ValueError: attempted relative import beyond top-level package

步骤

创建一个setup.py文件到根目录


setup.py的内容是*

from setuptools import setup, find_packages

setup(name='myproject', version='1.0', packages=find_packages())

使用虚拟环境


如果您熟悉虚拟环境,请激活一个,然后跳转到下一步。使用虚拟环境并不是绝对必需的,但从长远来看,它们确实会帮助你(当你有多个正在进行的项目时)。最基本的步骤是(在根文件夹中运行)

创建虚拟环境 Python -m venv venv 激活虚拟环境 ./venv/bin/activate (Linux, macOS)或./venv/Scripts/activate (Win)

要了解更多信息,请参考谷歌“python virtual env tutorial”或类似内容。除了创建、激活和取消激活之外,您可能永远不需要任何其他命令。

创建并激活虚拟环境后,控制台应该在括号中给出虚拟环境的名称

PS C:\tmp\test_imports> python -m venv venv
PS C:\tmp\test_imports> .\venv\Scripts\activate
(venv) PS C:\tmp\test_imports>

你的文件夹树应该看起来像这样**

.
├── myproject
│   ├── api
│   │   ├── api_key.py
│   │   ├── api.py
│   │   └── __init__.py
│   ├── examples
│   │   ├── example_one.py
│   │   ├── example_two.py
│   │   └── __init__.py
│   ├── LICENCE.md
│   ├── README.md
│   └── tests
│       ├── __init__.py
│       └── test_one.py
├── setup.py
└── venv
    ├── Include
    ├── Lib
    ├── pyvenv.cfg
    └── Scripts [87 entries exceeds filelimit, not opening dir]

PIP在可编辑状态下安装项目


使用pip安装你的顶级包myproject。诀窍是在进行安装时使用-e标志。通过这种方式,它被安装在可编辑状态下,对.py文件的所有编辑都将自动包含在已安装的包中。

在根目录下运行

PIP install -e。(注意这个点,它代表“当前目录”)

您还可以看到它是通过使用pip freeze安装的

(venv) PS C:\tmp\test_imports> pip install -e .
Obtaining file:///C:/tmp/test_imports
Installing collected packages: myproject
  Running setup.py develop for myproject
Successfully installed myproject
(venv) PS C:\tmp\test_imports> pip freeze
myproject==1.0

添加。。导入文件中


注意,您必须添加myproject。只能输入到其他无法工作的输入中。没有setup.py和pip安装的导入仍然可以正常工作。请参见下面的示例。


测试解决方案

现在,让我们使用上面定义的api.py和下面定义的test_one.py测试解决方案。

test_one.py

from myproject.api.api import function_from_api

def test_function():
    print(function_from_api())

if __name__ == '__main__':
    test_function()

运行测试

(venv) PS C:\tmp\test_imports> python .\myproject\tests\test_one.py
I am the return value from api.api!

*查看setuptools文档获取更多详细的setup.py示例。

**在现实中,您可以将虚拟环境放在硬盘上的任何地方。

对于兄弟包导入,您可以使用[sys. xml]的insert或append方法。路径][2]模块:

if __name__ == '__main__' and if __package__ is None:
    import sys
    from os import path
    sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
    import api

如果你启动你的脚本,这将工作如下:

python examples/example_one.py
python tests/test_one.py

另一方面,你也可以使用相对导入:

if __name__ == '__main__' and if __package__ is not None:
    import ..api.api

在这种情况下,你必须使用'-m'参数启动你的脚本(注意,在这种情况下,你不能给出'.py'扩展名):

python -m packageName.examples.example_one
python -m packageName.tests.test_one

当然,你可以混合使用这两种方法,这样你的脚本无论如何调用都能正常工作:

if __name__ == '__main__':
    if __package__ is None:
        import sys
        from os import path
        sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
        import api
    else:
        import ..api.api

项目

1.1用户

1.1.1 about.py

1.1.2 init.py

1.2技术

1.2.1 info.py

1.1.2 init.py

现在,如果你想访问User包中的about.py模块,从Tech包中的info.py模块,那么你必须将cmd(在windows中)路径带到项目中,即。 **C:\Users\Personal\Desktop\Project>**根据上面的包示例。你必须从这个路径输入python -m Package_name.module_name 例如,对于上面的包,我们必须做,

c:\用户\个人\桌面\项目>python -m Tech.info

小鬼点

不要在info模块后使用.py扩展名,即python -m Tech.info.py 输入this,其中兄弟包位于同一级别。 -m是标志,要检查它,你可以从CMD python——help