当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何在Python中进行相对导入?》经验,为你挑选了10个好方法。

想象一下这个目录结构:

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

我正在编码mod1,我需要从中导入一些东西mod2.我该怎么办?

我尝试了from ..sub2 import mod2但是我得到了"尝试非包装中的相对导入".

我google了一下,但发现只有" sys.path操纵"黑客.有没有干净的方式?


编辑:我__init__.py的所有人目前都是空的

EDIT2:我想这样做,因为SUB2包含了为子包(共享类sub1,subX等等).

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



1> John B..:

每个人似乎都想告诉你你应该做什么,而不仅仅是回答这个问题.

问题是你通过将mod1.py作为参数传递给解释器来运行模块'__main__'.

从PEP 328:

相对导入使用模块的__name__属性来确定模块在包层次结构中的位置.如果模块的名称不包含任何包信息(例如,它设置为'__main__'),则解析相对导入,就像模块是顶级模块一样,无论模块实际位于文件系统的哪个位置.

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

更新:根据Nick Coghlan的说法,推荐的替代方法是使用-m开关在包内运行模块.


我不明白:答案在哪里?如何在这样的目录结构中导入模块?
推荐的替代方法是使用`-m`开关在包内运行模块,而不是直接指定它们的文件名.
@Tom:在这个例子中,mod1将`来自sub2 import mod2`.然后,要从app中运行mod1,请执行`python -m sub1.mod1`.
@XiongChiamiov:这是否意味着如果您的python嵌入在应用程序中,则无法执行此操作,因此您无法访问python命令行开关?
这里的答案涉及在程序的每个入口点处弄乱sys.path.我想这是唯一的方法.
@MattJoiner:如果你将mod1.py作为`python -m app.sub1.mod1`(来自`app`的父目录)运行[Pankaj写道](http://stackoverflow.com/a/15458607/ 229381).
为了说清楚,当你从一个脚本中执行`from import for what`作为参数传递给解释器时,它基本上[解析](https://github.com/python/cpython/blob/v3.6.4/ Python/import.c#L1471)to` from __main __.m import whatever`.这使得它寻找`__main __/m.py`.

2> nosklo..:
main.py
setup.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       module_a.py
    package_b/ ->
       __init__.py
       module_b.py

    你跑python main.py.

    main.py 作用: import app.package_a.module_a

    module_a.pyimport app.package_b.module_b

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

只要你有appPYTHONPATH ,那就行了.main.py可能在任何地方.

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


建议的额外阅读:http://blog.habnab.it/blog/2013/07/21/python-packages-and-you/
然后,有一天,需要将app的名称更改为test_app.会发生什么?您需要更改所有源代码,导入app.package_b.module_b - > test_app.package_b.module_b.这绝对是不好的做法......我们应该尝试在包中使用相对导入.
很好的答案.有没有办法导入这种方式而不在PYTHONPATH中安装包?

3> Pankaj ..:

这是适用于我的解决方案:

我做相对导入 from ..sub2 import mod2 然后,如果我想运行mod1.py然后我转到父目录app并使用python -m开关运行模块as python -m app.sub1.mod1.

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

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

在进行相对导入时,我多次遇到过这个问题.并且,在阅读了之前的所有答案之后,我仍然无法以干净的方式弄清楚如何解决它,而无需在所有文件中放置样板代码.(虽然有些评论非常有用,感谢@ncoghlan和@XiongChiamiov)

希望这可以帮助那些与相对进口问题作斗争的人,因为通过PEP真的不好玩.


另请注意:这个答案是问题发生后的5年.这些功能当时不可用.
最佳答案恕我直言:不仅解释了为什么OP有这个问题,而且还找到了解决它的方法*而不改变他的模块进口方式*.毕竟,OP的相对进口情况良好.罪魁祸首是当直接作为脚本运行时缺乏对外部包的访问,`-m`被设计为解决.

4> lesnik..:

"Guido将包中的脚本视为反模式"(拒绝 PEP-3122)

我花了很多时间试图找到解决方案,在Stack Overflow上阅读相关帖子并对自己说"必须有更好的方法!".看起来没有.


注:已经提到[pep-366](http://python.org/dev/peps/pep-0366/)(与[pep-3122]同时创建(http://www.python.org/) dev/peps/pep-3122 /))提供相同的功能,但使用不同的向后兼容实现,即,如果你想在一个包中运行一个模块作为脚本*并且*在其中使用显式相对导入,那么你可以运行它使用`-m`开关:`python -m app.sub1.mod1`或从顶级脚本调用`app.sub1.mod1.main()`(例如,从setup.py中定义的setuptools的entry_points生成) ).

5> Роман Арсень..:

这是100%解决的:

应用程序/

main.py

设置/

local_setings.py

在app/main.py中导入设置/ local_setting.py:

main.py:

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


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


谢谢!所有的人都强迫我以不同的方式运行脚本,而不是告诉我如何在脚本中解决它。但是我不得不更改代码以使用sys.path.insert(0,“ ../settings”)然后使用from local_settings import *

6> iElectric..:
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

我正在使用此代码段从路径导入模块,希望有所帮助


可能sys.path.append(path)应替换为sys.path.insert(0,path),sys.path [-1]应替换为sys.path [0].否则,如果搜索路径中已存在具有相同名称的模块,则该函数将导入错误的模块.例如,如果当前目录中存在"some.py",则import_path("/ imports/some.py")将导入错误的文件.
我正在使用这个片段,结合imp模块(如[1]所述)效果很好.[1]:http://stackoverflow.com/questions/1096216/override-namespace-in-python/1096247#1096247

7> suhailvs..:

nosklo's用例子解释答案

注意:所有__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()

main.py

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,这就是你想要的.对??



8> Garrett Berg..:

不幸的是,这是一个sys.path hack,但它运行得很好.

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

我想做的是以下(我正在使用的模块是module3):

mymodule\
   __init__.py
   mymodule1\
      __init__.py
      mymodule1_1
   mymodule2\
      __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, '../..')

这样的黑客,但让一切工作!所以请记住,如果您希望您的决定覆盖其他路径,那么您需要使用sys.path.insert(0,pathname)来使其工作!这对我来说是一个非常令人沮丧的问题,很多人都说使用sysndpath的"append"函数,但是如果你已经定义了一个模块(我发现它非常奇怪的行为),这不起作用



9> milkypostman..:

我想把它放在这里供我自己参考.我知道它不是很好的Python代码,但我需要一个我正在处理的项目的脚本,我想将脚本放在一个scripts目录中.

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



10> LondonRob..:

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

import imp

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

这取自这个SO答案.

推荐阅读
sx-March23
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有