Python包从父目录导入

41

我有以下源代码结构

testapp/
├─ __init__.py
├─ testmsg.py
├─ sub/
│  ├─ __init__.py
│  ├─ testprinter.py

testmsg 定义了以下常量:

MSG = "Test message"

sub/testprinter.py

import testmsg

print("The message is: {0}".format(testmsg.MSG))

但是我得到了 ImportError: No module named testmsg 的错误。

既然按照包的结构应该可以工作,那么我不想在每个子模块中都扩展sys.path,也不想使用相对导入。

我在这里做错了什么?


@SimeonVisser 运行 python sub/testprinter.py,但是在 sub 目录中运行 python testprinter.py 也不起作用。 - user1543863
5个回答

28

这完全取决于你要运行哪个脚本。该脚本的路径将自动添加到Python的搜索路径中。

将其构建为以下结构:

TestApp/
├─ testapp/
│  ├─ __init__.py
│  ├─ testmsg.py
│  ├─ sub/
│  │  ├─ __init__.py
│  │  ├─ testprinter.py
├─ README
├─ LICENSE
├─ setup.py
├─ run_test.py

首先运行 TestApp/run_test.py 文件:

from testapp.sub.testprinter import functest ; functest()

那么TestApp/testapp/sub/testprinter.py可以执行:

from testapp.testmsg import MSG
print("The message is: {0}".format(testmsg.MSG))

这里还有更多好的提示:点击此处


这可能取决于我是否生成了setup.py文件。我的意思是,我遵循了Python包结构,但我没有生成安装方法,假设它也可以在没有setup.py install的情况下正常工作。 - user1543863
@CodeShining,这并不重要。我只是为了清晰起见在示例中包含了 setup.py。真正重要的是,就像我在答案的第一段中所说的那样,首先运行哪个文件才是最重要的。如果你在包内部启动(运行)一个文件,它将看不到该包。因此,最好将主脚本(你要运行的那个脚本)放在包外面,这是一个好习惯。 - nosklo

15

像下面这样使用相对导入

from .. import testmsg

但作为一个包,它不应该按预期工作吗?如果希望它正常工作,我不会使用相对路径。 - user1543863
1
CodeShining,请查看Dido关于 Python 3.5 PEP-328 标准的决定。[链接](http://www.python.org/dev/peps/pep-0328/#guido-s-decision) - Vinayak Kolagi
51
ValueError: 尝试相对于顶层包以外的位置进行导入。 - Cameron Hudson
@CameronHudson,相邻的文件夹也需要成为一个包(即拥有自己的__init__.py文件)。 - Jan Kukacka

14

对于仍然有同样问题的人。这是我解决我的方法:

import unittest 
import sys
import os

sys.path.append(os.getcwd() + '/..')

from my_module.calc import *

1
这拯救了我的一天,我已经搜索了整个过程但没有结果,谢谢 :) - Houy Narun
2
cwd 不总是正在运行的脚本的实际父目录,而正在运行的脚本才是您当前的顶层。如果您想要干涉 sys.path,则应该使用 __file__ 的父目录,而不是 cwd 的父目录。 - Soul Reaver
正如@SoulReaver所指出的那样,最好执行sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir))(或在Python3中使用pathlib模块)。 - Andre Holzner

11

这个问题的答案是 - 动态导入:

如何在父目录中导入Python文件

import sys
sys.path.append(path_to_parent)
import parent.file1

我写了一个可以导入任何东西的脚本。当然,你仍然需要把这个脚本复制到本地目录中,进行导入,并使用你想要的路径。

import sys
import os

# a function that can be used to import a python module from anywhere - even parent directories
def use(path):
    scriptDirectory = os.path.dirname(sys.argv[0])  # this is necessary to allow drag and drop (over the script) to work
    importPath = os.path.dirname(path)
    importModule = os.path.basename(path)
    sys.path.append(scriptDirectory+"\\"+importPath)        # Effing mess you have to go through to get python to import from a parent directory

    module = __import__(importModule)
    for attr in dir(module):
        if not attr.startswith('_'):
            __builtins__[attr] = getattr(module, attr)

2

试试这个:


import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(__file__)))

from my_module import *


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接