想象一下这个目录结构:

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

我正在编写mod1,我需要从mod2中导入一些东西。我该怎么做呢?

我试过…sub2导入mod2,但我得到了一个“尝试相对导入非包”。

我搜索了一下,但只找到了“sys”。路径操纵“黑客”。没有干净的方法吗?


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

Edit2:我试图这样做是因为sub2包含跨子包共享的类(sub1、subX等)。

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


当前回答

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

我有同样的问题,PEP 328或366都没有完全解决这个问题,因为两者在一天结束时,都需要包的头包含在sys. exe中。据我所知,这是一条道路。

我还应该提到,我没有找到如何格式化字符串应该进入这些变量。是package_head.subfolder吗?Module_name”或者什么?

其他回答

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

问题是,通过将mod1.py作为参数传递给解释器,您正在以'__main__'的身份运行模块。

PEP 328:

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

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

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

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执行如下操作:导入app.package_a.module_a Module_a.py会导入app.package_b.module_b

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

只要你的PYTHONPATH中有app,这就可以工作。Main.py可以在任何地方。

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

正如@EvgeniSergeev在OP的注释中所说,你可以从.py文件中任意位置导入代码:

import imp

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

这是从这个SO答案中得来的。

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

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

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

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

我使用这个代码片段从路径导入模块,希望有帮助