576

想象一下这个目录结构:

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

我正在编码mod1,我需要从mod2. 我该怎么做?

我试过from ..sub2 import mod2了,但我得到了一个“尝试在非包中进行相对导入”。

我四处搜索,但只发现“sys.path操纵”黑客。没有干净的方法吗?


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

Edit2:我正在尝试这样做,因为 sub2 包含跨子包(,等)共享的sub1subX

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

4

18 回答 18

368

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

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

来自PEP 328

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

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

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

于 2008-09-16T14:48:56.873 回答
150

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

我执行相对导入 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 真的不好玩。

于 2013-03-17T07:43:34.060 回答
128
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目标系统的脚本文件夹。

于 2009-01-21T12:42:11.840 回答
51

“Guido 将包中的运行脚本视为反模式”(拒绝 PEP-3122

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

于 2011-11-19T16:05:29.897 回答
43

这是100%解决的:

  • 应用程序/
    • 主文件
  • 设置/
    • local_settings.py

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

主要.py:

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


try:
    from local_settings import *
except ImportError:
    print('No Import')
于 2016-02-11T12:01:50.573 回答
26

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()

主文件

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

于 2013-12-08T03:19:42.223 回答
24
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

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

于 2009-07-04T23:27:50.467 回答
12

不幸的是,这是一个 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) 让它工作!这对我来说是一个非常令人沮丧的症结,很多人说要对 sys.path 使用“附加”功能,但是如果您已经定义了一个模块,那将不起作用(我觉得这是非常奇怪的行为)

于 2012-03-02T22:58:47.093 回答
11

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

import os.path
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
于 2013-01-07T04:26:52.227 回答
9

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

import imp

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

这是取自这个 SO answer

于 2015-06-09T15:22:27.963 回答
7

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

from .mod1 import stuff
于 2011-09-24T19:31:57.547 回答
2

来自Python 文档

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

于 2011-06-29T17:33:27.220 回答
1

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

bash$ export PYTHONPATH=/PATH/TO/APP

然后:

import sub1.func1
#...more import

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

于 2012-03-17T09:20:09.027 回答
1

除了 John B 所说的之外,似乎设置__package__变量应该会有所帮助,而不是改变__main__可能会搞砸其他事情的变量。但据我测试,它并不能完全发挥应有的作用。

sys.path我有同样的问题,PEP 328 或 366 都没有完全解决问题,因为据我所知,到一天结束时,都需要将包的头部包含在 中。

我还应该提到,我没有找到如何格式化应该进入这些变量的字符串。是它"package_head.subfolder.module_name"还是什么?

于 2012-09-11T07:47:16.920 回答
1

您必须将模块的路径附加到PYTHONPATH

export PYTHONPATH="${PYTHONPATH}:/path/to/your/module/"
于 2020-03-25T10:13:17.370 回答
0

类似于 Роман Арсеньев 的回答,但如果你想回到两个目录:

返回两个目录:

app/
   __init__.py
   sub1/
      __init__.py
      mod1.py          ------*
      subsub1/               |
         __init__.py   mod1 and mod4  
         mod4.py       ------|
   sub2/                     |
      __init__.py           into
      mod2.py                |
      subsub2/              mod3
         __init__.py         |
           mod3.py     <-----*

将 mod1 导入 mod3:

import sys
sys.path.insert(1, '../../')

from sub1 import mod1
from sub1.subsub1 import mod
于 2021-11-27T20:29:26.053 回答
0

此方法查询并自动填充路径:

import os
import inspect
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
os.sys.path.insert(1, parentdir)
# print("currentdir = ", currentdir)
# print("parentdir=", parentdir)
于 2021-11-30T02:32:55.737 回答
0

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

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

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

于 2022-03-01T22:56:03.180 回答