如何在 Python 中进行相对导入?

2024-11-21 08:33:00
admin
原创
5
摘要:问题描述:想象一下这样的目录结构:app/ __init__.py sub1/ __init__.py mod1.py sub2/ __init__.py mod2.py 我正在编写代码mod1,需要从 导入一些内容mod2。我该怎么做?我尝试...

问题描述:

想象一下这样的目录结构:

app/
   __init__.py
   sub1/
      __init__.py
      mod1.py
   sub2/
      __init__.py
      mod2.py

我正在编写代码mod1,需要从 导入一些内容mod2。我该怎么做?

我尝试过from ..sub2 import mod2,但是收到“尝试在非包中进行相对导入”的错误提示。

我在 Google 上搜索了一下,但只找到了“sys.path操纵”黑客。难道没有干净的方法吗?


我的所有__init__.py都是空的

我尝试这样做是因为 sub2 包含在子包之间共享的类(sub1subX等)。

我正在寻找的行为与PEP 366中描述的行为相同(感谢 John B)。


解决方案 1:

问题在于您通过将 mod1.py 作为参数传递给解释器,将模块作为“__main__”运行。

来自PEP 328:

相对导入使用模块的 name 属性来确定该模块在包层次结构中的位置。如果模块的名称不包含任何包信息(例如,它被设置为“__main__”),则相对导入将被解析为模块是顶级模块,而不管模块在文件系统上的实际位置。

在 Python 2.6 中,他们添加了相对于主模块引用模块的功能。PEP 366描述了这一变化。

解决方案 2:

这是对我有用的解决方案:

我进行相对导入 from ..sub2 import mod2
,然后,如果我想运行mod1.py,那么我会转到的父目录app并使用 python -m 开关以 方式运行该模块 python -m app.sub1.mod1

相对导入出现此问题的真正原因是,相对导入通过获取__name__模块的属性来工作。如果直接运行模块,则__name__设置为__main__,并且它不包含有关包结构的任何信息。这就是 python 抱怨relative import in non-package错误的原因。

因此,通过使用 -m 开关,您可以向 python 提供包结构信息,通过该信息它可以成功解析相对导入。

我在执行相对导入时多次遇到过这个问题。而且,在阅读了所有之前的答案之后,我仍然无法弄清楚如何以一种干净的方式解决这个问题,而无需在所有文件中放入样板代码。(尽管有些评论确实很有帮助,感谢@ncoghlan 和@XiongChiamiov)

希望这能帮助那些正在与相对导入问题作斗争的人,因为经历 PEP 真的并不好玩。

解决方案 3:

main.py
setup.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       module_a.py
    package_b/ ->
       __init__.py
       module_b.py
  1. 你跑python main.py

  2. main.py做:import app.package_a.module_a

  3. module_a.pyimport app.package_b.module_b

或者 2 或 3 可以使用:from app.package_a import module_a

只要您app的 PYTHONPATH 中有它,它就会起作用。main.py它可以在任何地方。

因此,您编写一个程序setup.py来将整个应用程序包和子包复制(安装)到目标系统的 python 文件夹以及main.py目标系统的脚本文件夹。

解决方案 4:

“Guido 认为在包中运行脚本是一种反模式”(拒绝
PEP-3122)

我花了很多时间试图找到解决方案,阅读 Stack Overflow 上的相关文章,并对自己说“一定有更好的方法!”。看来没有。

解决方案 5:

这个问题已经 100% 解决了:

  • 应用程序/

+ 主程序
  • 设置/

+ 本地设置.py

在 app/main.py 中导入 settings/local_setting.py:

主要文件:

import sys
sys.path.insert(0, "../settings")


try:
    from local_settings import *
except ImportError:
    print('No Import')

解决方案 6:

举例说明nosklo 的回答:

注意:所有__init__.py文件都是空的。

main.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       fun_a.py
    package_b/ ->
       __init__.py
       fun_b.py

应用程序/package_a/fun_a.py

def print_a():
    print 'This is a function in dir package_a'

应用程序/package_b/fun_b.py

from app.package_a.fun_a import print_a
def print_b():
    print 'This is a function in dir package_b'
    print 'going to call a function in dir package_a'
    print '-'*30
    print_a()

主程序

from app.package_b import fun_b
fun_b.print_b()

如果你运行python main.py它,它将返回:

This is a function in dir package_b
going to call a function in dir package_a
------------------------------
This is a function in dir package_a
  • main.py 的作用是:from app.package_b import fun_b

  • fun_b.py 确实from app.package_a.fun_a import print_a

因此文件夹中的文件package_b使用了文件夹中的文件package_a,这正是您想要的。对吗?

解决方案 7:

使用:

def import_path(fullpath):
    """ 
    Import a file with full path specification. Allows one to
    import from anywhere, something __import__ does not do. 
    """
    path, filename = os.path.split(fullpath)
    filename, ext = os.path.splitext(filename)
    sys.path.append(path)
    module = __import__(filename)
    reload(module) # Might be out of date
    del sys.path[-1]
    return module

我正在使用此代码片段从路径导入模块。

解决方案 8:

不幸的是,这是一个 sys.path 黑客攻击,但它运行得很好。

我在另一层遇到了这个问题:我已经有一个指定名称的模块,但它是错误的模块。

我想要做的事情如下(我工作的模块是 module3):

mymodule\n   __init__.py
   mymodule1\n      __init__.py
      mymodule1_1
   mymodule2\n      __init__.py
      mymodule2_1


import mymodule.mymodule1.mymodule1_1  

请注意,我已经安装了 mymodule,但在我的安装中没有“mymodule1”

并且我会收到一个 ImportError 因为它试图从我已安装的模块中导入。

我尝试执行 sys.path.append,但不起作用。起作用的是sys.path.insert

if __name__ == '__main__':
    sys.path.insert(0, '../..')

有点像 hack,但可以让它正常工作!所以请记住,如果您希望您的决定覆盖其他路径,则需要使用 sys.path.insert(0, pathname) 来使其工作!这对我来说是一个非常令人沮丧的症结,很多人说要使用“append”函数到 sys.path,但如果已经定义了一个模块,则不起作用(我发现这是非常奇怪的行为)

解决方案 9:

我把这个放在这里仅供自己参考。我知道这不是好的 Python 代码,但我需要一个用于我正在做的项目的脚本,我想把这个脚本放在一个scripts目录中。

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

解决方案 10:

看看http://docs.python.org/whatsnew/2.5.html#pep-328-absolute-and-relative-imports。你可以这样做

from .mod1 import stuff

解决方案 11:

正如EvgeniSergeev在对 OP 的评论中所说,你可以.py使用以下命令从任意位置的文件导入代码:

import imp

foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()

这是从这个 SO 答案中摘取的。

解决方案 12:

来自Python 文档,

在 Python 2.5 中,您可以使用指令将导入的行为切换为绝对导入from __future__ import absolute_import。这种绝对导入行为将成为未来版本(可能是 Python 2.7)的默认行为。一旦绝对导入成为默认行为,import string将始终查找标准库的版本。建议用户尽可能开始使用绝对导入,因此最好from pkg import string在代码中开始编写

解决方案 13:

除了John B 所说的,设置变量似乎__package__应该有帮助,而不是更改变量__main__,这可能会搞砸其他事情。但据我测试,它并没有完全按预期工作。

我有同样的问题,据我所知,PEP 328和PEP 366都不能完全解决问题,因为到最后,两者都需要将包的头部包含在内。sys.path

解决方案 14:

我发现将“PYTHONPATH”环境变量设置为顶级文件夹更容易:

bash$ export PYTHONPATH=/PATH/TO/APP

然后:

import sub1.func1
# ...more imports

当然,PYTHONPATH 是“全局的”,但它还没有给我带来麻烦。

解决方案 15:

一个不简单的方法是在运行时将当前目录附加到 PATH,如下所示:

import pathlib   
import sys
sys.path.append(pathlib.Path(__file__).parent.resolve())
import file_to_import  # the actual intended import

与该问题的另一种解决方案相反,此解决方案使用pathlib而不是os.path

解决方案 16:

多么激烈的争论啊!

我对 Python 还比较陌生(但有多年的编程经验,并且不喜欢Perl),对Apache设置的黑暗艺术也比较陌生,但我知道我(认为我)需要什么才能让我的小实验项目在家里运行起来。

以下是我对目前情况的总结。

如果我使用 -m '模块' 方法,我需要:

  1. 把一切点在一起;

  2. 从父文件夹运行它;

  3. 丢失‘.py’​​;

  4. 在每个子文件夹中创建一个空的(!)__init__.py文件。

在CGI环境中,如何工作?我已为脚本目录设置了别名,并想直接以/dirAlias/cgi_script.py的形式运行脚本?

为什么修改sys.path是一种 hack?Python 文档页面指出:“程序可以根据自己的目的随意修改此列表。”如果它有效,它就有效,对吧?Accounts 中的 bean 计数器不关心它是如何工作的。

只想向上一级然后向下进入“模块”目录:

.../py
      /cgi
      /build
      /modules

所以我的“模块”可以从 CGI 世界或服务器世界导入。

我尝试过-m/modules 方法,但我认为我更喜欢以下方法(并且并不困惑如何在 CGI 空间中运行它):

  1. XX_pathsetup.py/path/to/python/Lib目录(或默认sys.path列表中的任何其他目录)中创建。'XX' 是一些标识符,它声明了根据文件中的规则设置我的路径的意图。

  2. 在任何想要从上述目录配置中的“模块”目录导入的脚本中,只需import XX_pathsetup.py

下面是我的非常简单的XX_pathsetup.py文件:

import sys, os
pypath = sys.path[0].rsplit(os.sep, 1)[0]
sys.path.insert(0, pypath + os.sep + 'modules')

在我看来,这不是一个“黑客行为”。这是一个放在 Python“Lib”目录中的小文件和一个声明修改路径搜索顺序意图的导入语句。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用