兄弟包导入

如何解决兄弟包导入

| 我尝试阅读有关同级进口甚至进口商品的问题 包文档,但我还没有找到答案。 具有以下结构:
├── 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
examples
tests
目录中的脚本如何从
api
模块并从命令行运行? 另外,我想避免每个文件的丑陋的
sys.path.insert
hack。一定 这可以用Python完成,对吧?     

解决方法

        七年后 自从我在下面写下答案以来,修改ѭ5仍然是一种快速技巧,适用于私有脚本,但是有一些改进 安装软件包(无论是否在virtualenv中)都可以满足您的要求,尽管我建议使用pip而不是直接使用setuptools(并使用
setup.cfg
存储元数据) 使用
-m
标志并作为软件包运行也可以(但如果要将工作目录转换为可安装的软件包,会有些尴尬)。 对于测试,尤其是pytest在这种情况下能够找到api包,并为您解决
sys.path
的黑客问题 因此,这实际上取决于您要做什么。但是,对于您而言,由于您的目标似乎是在某个时候制作一个合适的软件包,因此即使不很完美,通过
pip -e
安装也可能是您的最佳选择。 旧答案 正如其他地方已经提到的那样,可怕的事实是,您必须进行丑陋的修改,才能允许从同级模块或“ or10”模块的父级包中导入。 PEP 366中对此问题进行了详细说明。PEP3122试图以一种更合理的方式处理进口,但是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
path[0]
是运行脚本的父文件夹,
dir(path[0])
是顶层文件夹。 虽然我仍然不能使用相对导入,但是它确实允许从顶层(在您的示例
api
\的父文件夹中)进行绝对导入。     ,厌倦了sys.path hacks? 有许多ѭ15的-hacks,但我发现了另一种解决手头问题的方法:setuptools。我不确定是否存在无法很好解决的情况。以下内容已在Windows 10机器的Python 3.6.5(Anaconda,conda 4.5.1)中进行了测试。 设定 起点是您提供的文件结构,包装在名为“ 16”的文件夹中。
.
└── 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
我将把“ 18”称为根文件夹,在我的示例中,它位于“ 19”。 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 import function_from_api
会导致
PS C:\\tmp\\test_imports> python .\\myproject\\tests\\test_one.py
Traceback (most recent call last):
  File \".\\tests\\test_one.py\",in <module>
    from ..api.api import function_from_api
ValueError: attempted relative import beyond top-level package
脚步 1)将setup.py文件创建到根目录
setup.py
的内容为*
from setuptools import setup,find_packages

setup(name=\'myproject\',version=\'1.0\',packages=find_packages())
2)使用虚拟环境 如果您熟悉虚拟环境,请激活一个,然后跳到下一步。并非绝对需要使用虚拟环境,但从长远来看(当您正在进行多个项目时),它们确实可以帮助您。最基本的步骤是(在根文件夹中运行) 创建虚拟环境
python -m venv venv
激活虚拟环境
source ./venv/bin/activate
(Linux,macOS)或
./venv/Scripts/activate
(Win) 要了解有关此内容的更多信息,只需在Google上搜索“ python虚拟环境教程”或类似内容即可。除了创建,激活和停用之外,您可能根本不需要任何其他命令。 创建并激活虚拟环境后,控制台应在括号中提供虚拟环境的名称。
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]
3)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
4)在您的进口商品中加
myproject.
请注意,您只需要在导入中添加
myproject.
,否则将无法正常工作。没有
setup.py
pip install
的进口产品仍然可以正常工作。请参阅下面的示例。 测试解决方案 现在,让我们使用上面定义的
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!
*有关更多详细的setup.py示例,请参阅setuptools文档。 **实际上,您可以将虚拟环境放在硬盘上的任何位置。     ,        这是我在
tests
文件夹中的Python文件顶部插入的另一种选择:
# Path hack.
import sys,os
sys.path.insert(0,os.path.abspath(\'..\'))
    ,        除非有必要,否则您不需要并且不应该破解
sys.path
,在这种情况下则不是。采用:
import api.api_key # in tests,examples
从项目目录中运行:
python -m tests.test_one
。 您可能应该将
tests
(如果它们是api的单元测试)移到
api
内,然后运行
python -m api.test
运行所有测试(假设有
__main__.py
)或or55ѭ运行run56ѭ。 您也可以从
examples
中删除
__init__.py
(这不是Python软件包),然后在安装了
api
的virtualenv中运行示例,例如,如果您有适当的
setup.py
,则在virtualenv中的
pip install -e .
会安装就地
api
软件包。     ,        我还没有对Python的理解,没有看到在兄弟姐妹/相对导入黑客之间不相关项目之间共享代码的预期方式所必需的。直到那天,这是我的解决方案。对于
examples
tests
..\\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
    ,        对于同级包导入,可以使用[sys.path] [2]模块的insert或append方法:
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
    ,TLDR 此方法不需要setuptools,path hacks,其他命令行参数或在项目的每个文件中指定包的顶层。 只需在要调用的10父目录中创建一个脚本,然后从那里运行所有内容。有关进一步的说明,请继续阅读。 说明 这可以实现,而无需一起破解新路径,使用额外的命令行参数或向您的每个程序添加代码以识别其兄弟姐妹。 正如我之前提到的那样,此操作失败的原因是被调用的程序将其“ 73”设置为“ 10”。发生这种情况时,被调用的脚本会接受自身位于程序包的顶层,并拒绝识别兄弟目录中的脚本。 但是,目录顶层下的所有内容仍然可以识别顶层下的任何内容。这意味着,要使同级目录中的文件相互识别/利用,您只需要做的就是从其父目录中的脚本中调用它们。 概念证明 在具有以下结构的目录中:
.
|__Main.py
|
|__Siblings
   |
   |___sib1
   |   |
   |   |__call.py
   |
   |___sib2
       |
       |__callsib.py
Main.py
包含以下代码:
import sib1.call as call


def main():
    call.Call()


if __name__ == \'__main__\':
    main()
sib1 / call.py包含:
import sib2.callsib as callsib


def Call():
    callsib.CallSib()


if __name__ == \'__main__\':
    Call()
sib2 / callsib.py包含:
def CallSib():
    print(\"Got Called\")

if __name__ == \'__main__\':
    CallSib()
如果重现此示例,您将注意到调用
Main.py
将导致按照“81ѭ的定义打印\” Got Called \“,即使
sib2/callsib.py
是通过
sib1/call.py
调用的。但是,如果要直接调用ѭ83(在对导入进行了适当的更改之后),则会引发异常。即使它由其父目录中的脚本调用时可以工作,但如果它认为自己位于程序包的顶层,则它将无法工作。     ,        您需要查看如何在相关代码中编写导入语句。如果
examples/example_one.py
使用以下导入语句:
import api.api
...然后,它希望项目的根目录位于系统路径中。 没有任何黑客(如您所说)来支持此操作的最简单方法是从顶层目录运行示例,如下所示:
PYTHONPATH=$PYTHONPATH:. python examples/example_one.py 
    ,        以防万一有人在Eclipse上使用Pydev的情况出现在这里:您可以使用Project-> Properties并在左侧下方设置External Libraries,将同级的父路径(以及调用模块的父路径)添加为外部库文件夹。菜单Pydev-PYTHONPATH。然后,您可以从同级导入,例如G。
from sibling import some_class
。     ,        我制作了一个示例项目来演示如何处理此问题,这确实是如上所述的另一个sys.path hack。 Python同级导入示例,该示例依赖于:
if __name__ == \'__main__\':
    import os
    import sys
    sys.path.append(os.getcwd())
只要您的工作目录位于Python项目的根目录下,这似乎就非常有效。如果有人将其部署在实际的生产环境中,那么很高兴听到它是否也可以在这里正常工作。     ,        首先,应避免使用与模块本身同名的文件。它可能会破坏其他进口。 导入文件时,首先解释器检查当前目录,然后搜索全局目录。 在
examples
tests
中,您可以致电:
from ..api import api
    

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-