我想从同一目录中的另一个文件导入一个函数。
有时候from .mymodule import myfunction
对我有用,但有时我会得到:
SystemError: Parent module '' not loaded, cannot perform relative import
有时它适用于from mymodule import myfunction
,但有时我也会得到:
SystemError: Parent module '' not loaded, cannot perform relative import
我不明白这里的逻辑,我找不到任何解释。这看起来完全随机。
有人可以向我解释这一切背后的逻辑是什么吗?
答案 0 :(得分:404)
不幸的是,这个模块需要在包内,它也是 有时需要作为脚本运行。知道我怎么做 实现那个?
这样的布局很常见......
main.py
mypackage/
__init__.py
mymodule.py
myothermodule.py
......像这样的mymodule.py
......
#!/usr/bin/env python3
# Exported function
def as_int(a):
return int(a)
# Test function for module
def _test():
assert as_int('1') == 1
if __name__ == '__main__':
_test()
...这样myothermodule.py
......
#!/usr/bin/env python3
from .mymodule import as_int
# Exported function
def add(a, b):
return as_int(a) + as_int(b)
# Test function for module
def _test():
assert add('1', '1') == 2
if __name__ == '__main__':
_test()
......和main.py
这样......
#!/usr/bin/env python3
from mypackage.myothermodule import add
def main():
print(add('1', '1'))
if __name__ == '__main__':
main()
...当您运行main.py
或mypackage/mymodule.py
时效果正常,但由于相对导入而失败并显示mypackage/myothermodule.py
...
from .mymodule import as_int
你应该运行它的方式是......
python3 -m mypackage.myothermodule
...但它有点冗长,并且与#!/usr/bin/env python3
之类的shebang系列不能很好地融合。
对于这种情况最简单的修复,假设名称mymodule
是全局唯一的,将避免使用相对导入,只使用...
from mymodule import as_int
...虽然,如果它不是唯一的,或者你的包结构更复杂,你需要在PYTHONPATH
中包含包含你的包目录的目录,并且这样做......
from mypackage.mymodule import as_int
...或者,如果您希望它“开箱即用”,您可以先使用此代码在代码中PYTHONPATH
...
import sys
import os
PACKAGE_PARENT = '..'
SCRIPT_DIR = os.path.dirname(os.path.realpath(os.path.join(os.getcwd(), os.path.expanduser(__file__))))
sys.path.append(os.path.normpath(os.path.join(SCRIPT_DIR, PACKAGE_PARENT)))
from mypackage.mymodule import as_int
这是一种痛苦,但是有一个线索,为什么在某个Guido van Rossum写的an email中......
我在这个以及
__main__
提出的任何其他提议中都是-1 机械。唯一的用例似乎是运行脚本 住在模块的目录中,我一直认为这是一个目录 反模式。为了让我改变主意,你必须让我相信 它不是。
在包内运行脚本是否是反模式是主观的,但我个人觉得它在包含一些自定义wxPython小部件的包中非常有用,所以我可以运行脚本来显示任何源文件wx.Frame
仅包含用于测试目的的小部件。
答案 1 :(得分:182)
来自PEP 328
相对导入使用模块的__name__属性来确定 模块在包层次结构中的位置。如果模块的名称有 不包含任何包裹信息(例如,它设置为' __ main __') 然后解析相对导入,就像模块是顶级一样 模块,无论模块实际位于文件的何处 系统
...相对导入依赖于 __ name __ 来确定当前 模块在包层次结构中的位置。在一个主要模块中, __ name __ 的值始终为' __ main __' ,因此显式相对导入 将永远失败(因为它们只适用于包内的模块)
为了解决这个问题,PEP 366引入了顶级变量__package__
:
通过添加新的模块级属性,此PEP允许相对 如果使用 -m 执行模块,则导入自动工作 开关。模块本身允许少量样板 当按名称执行文件时,相对导入工作。 [...]当[属性]存在时,相对导入将基于此属性 而不是模块 __ name __ 属性。 [...]当主模块由其文件名指定时, __ package __ 属性将设置为 None 。 [...] 当导入系统遇到明确的相对导入时 没有__package__设置的模块(或者设置为None),它会 计算并存储正确的值( __ name __。rpartition('。')[0] 对于普通模块和 __ name __ ,用于软件包初始化模块)
(强调我的)
如果__name__
为'__main__'
,则__name__.rpartition('.')[0]
将返回空字符串。这就是错误描述中出现空字符串文字的原因:
SystemError: Parent module '' not loaded, cannot perform relative import
CPython PyImport_ImportModuleLevelObject
function的相关部分:
if (PyDict_GetItem(interp->modules, package) == NULL) {
PyErr_Format(PyExc_SystemError,
"Parent module %R not loaded, cannot perform relative "
"import", package);
goto error;
}
如果CPython无法在package
中找到interp->modules
(包的名称)(可以sys.modules
访问),则会引发此异常。由于sys.modules
是"一个字典,它将模块名称映射到已经加载的模块" ,现在很清楚父模块必须在执行相对导入之前显式绝对导入。
注意: issue 18018中的补丁添加了another if
block,在代码之前执行以上:
if (PyUnicode_CompareWithASCIIString(package, "") == 0) {
PyErr_SetString(PyExc_ImportError,
"attempted relative import with no known parent package");
goto error;
} /* else if (PyDict_GetItem(interp->modules, package) == NULL) {
...
*/
如果package
(与上面相同)为空字符串,则错误消息为
ImportError: attempted relative import with no known parent package
但是,您只能在Python 3.6或更新版本中看到这一点。
考虑一个目录(这是一个Python package):
.
├── package
│ ├── __init__.py
│ ├── module.py
│ └── standalone.py
package 中的所有文件都以相同的2行代码开头:
from pathlib import Path
print('Running' if __name__ == '__main__' else 'Importing', Path(__file__).resolve())
我包含这两行仅以使操作顺序明显。我们可以完全忽略它们,因为它们不会影响执行。
__ init __。py 和 module.py 只包含这两行(即它们实际上是空的)。
standalone.py 还会尝试通过相对导入导入 module.py :
from . import module # explicit relative import
我们很清楚/path/to/python/interpreter package/standalone.py
会失败。但是,我们可以使用-m
command line option运行模块,"搜索sys.path
以获取指定模块,并将其内容作为__main__
模块执行" :
vaultah@base:~$ python3 -i -m package.standalone
Importing /home/vaultah/package/__init__.py
Running /home/vaultah/package/standalone.py
Importing /home/vaultah/package/module.py
>>> __file__
'/home/vaultah/package/standalone.py'
>>> __package__
'package'
>>> # The __package__ has been correctly set and module.py has been imported.
... # What's inside sys.modules?
... import sys
>>> sys.modules['__main__']
<module 'package.standalone' from '/home/vaultah/package/standalone.py'>
>>> sys.modules['package.module']
<module 'package.module' from '/home/vaultah/package/module.py'>
>>> sys.modules['package']
<module 'package' from '/home/vaultah/package/__init__.py'>
-m
会为您完成所有导入内容并自动设置__package__
,但您可以在
请将其视为概念证明而非实际解决方案。它不适合在真实世界的代码中使用。
PEP 366有解决此问题的方法,但是,它不完整,因为单独设置__package__
是不够的。您将需要在模块层次结构中至少导入 N 前面的包,其中 N 是父目录的数量(相对于脚本的目录) )将搜索正在导入的模块。
因此,
将当前模块的第N 前任的父目录添加到sys.path
从sys.path
使用其完全限定名称
将__package__
设置为 2
执行相对导入
我将从解决方案#1 借用文件并添加更多子包:
package
├── __init__.py
├── module.py
└── subpackage
├── __init__.py
└── subsubpackage
├── __init__.py
└── standalone.py
这次 standalone.py 将使用以下相对导入从包包导入 module.py
from ... import module # N = 3
我们需要在该行之前加上样板代码,以使其有效。
import sys
from pathlib import Path
if __name__ == '__main__' and __package__ is None:
file = Path(__file__).resolve()
parent, top = file.parent, file.parents[3]
sys.path.append(str(top))
try:
sys.path.remove(str(parent))
except ValueError: # Already removed
pass
import package.subpackage.subsubpackage
__package__ = 'package.subpackage.subsubpackage'
from ... import module # N = 3
它允许我们按文件名执行 standalone.py :
vaultah@base:~$ python3 package/subpackage/subsubpackage/standalone.py
Running /home/vaultah/package/subpackage/subsubpackage/standalone.py
Importing /home/vaultah/package/__init__.py
Importing /home/vaultah/package/subpackage/__init__.py
Importing /home/vaultah/package/subpackage/subsubpackage/__init__.py
Importing /home/vaultah/package/module.py
可以找到包含在函数中的更通用的解决方案here。用法示例:
if __name__ == '__main__' and __package__ is None:
import_parents(level=3) # N = 3
from ... import module
from ...module.submodule import thing
步骤是 -
将显式相对导入替换为等效绝对导入
安装package
以使其可导入
例如,目录结构可能如下
.
├── project
│ ├── package
│ │ ├── __init__.py
│ │ ├── module.py
│ │ └── standalone.py
│ └── setup.py
其中 setup.py 是
from setuptools import setup, find_packages
setup(
name = 'your_package_name',
packages = find_packages(),
)
其余文件来自 Solution#1 。
安装将允许您导入包,无论您的工作目录如何(假设没有命名问题)。
我们可以修改 standalone.py 以使用此优势(步骤1):
from package import module # absolute import
将您的工作目录更改为project
并运行/path/to/python/interpreter setup.py install --user
(--user
在your site-packages directory中安装该软件包)(步骤2):
vaultah@base:~$ cd project
vaultah@base:~/project$ python3 setup.py install --user
让我们验证现在可以将 standalone.py 作为脚本运行:
vaultah@base:~/project$ python3 -i package/standalone.py
Running /home/vaultah/project/package/standalone.py
Importing /home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/__init__.py
Importing /home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/module.py
>>> module
<module 'package.module' from '/home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/module.py'>
>>> import sys
>>> sys.modules['package']
<module 'package' from '/home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/__init__.py'>
>>> sys.modules['package.module']
<module 'package.module' from '/home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/module.py'>
注意 :如果您决定沿着这条路走下去,最好使用virtual environments来隔离安装软件包。
坦率地说,安装不是必需的 - 您可以在脚本中添加一些样板代码,以使绝对导入有效。
我要从解决方案#1 借用文件并更改 standalone.py :
在尝试使用绝对导入从 package 导入任何内容之前,将 package 的父目录添加到sys.path
:
import sys
from pathlib import Path # if you haven't already done so
file = Path(__file__).resolve()
parent, root = file.parent, file.parents[1]
sys.path.append(str(root))
# Additionally remove the current file's directory from sys.path
try:
sys.path.remove(str(parent))
except ValueError: # Already removed
pass
用绝对导入替换相对导入:
from package import module # absolute import
standalone.py 运行没有问题:
vaultah@base:~$ python3 -i package/standalone.py
Running /home/vaultah/package/standalone.py
Importing /home/vaultah/package/__init__.py
Importing /home/vaultah/package/module.py
>>> module
<module 'package.module' from '/home/vaultah/package/module.py'>
>>> import sys
>>> sys.modules['package']
<module 'package' from '/home/vaultah/package/__init__.py'>
>>> sys.modules['package.module']
<module 'package.module' from '/home/vaultah/package/module.py'>
我觉得我应该警告你:如果您的项目结构复杂,请尽量不要这样做,尤其是。
作为附注,PEP 8建议使用绝对导入,但声明在某些情况下可以接受明确的相对导入:
建议使用绝对导入,因为它们通常更具可读性 并且倾向于表现得更好(或者至少提供更好的错误 消息)。 [...]但是,明确的相对进口是可以接受的 替代绝对进口,特别是在处理复杂时 使用绝对导入的包布局是不必要的 冗长。
答案 2 :(得分:39)
将此内容放入您的软件包的__init__.py文件:
# For relative imports to work in Python 3.6
import os, sys; sys.path.append(os.path.dirname(os.path.realpath(__file__)))
假设你的包装是这样的:
├── project
│ ├── package
│ │ ├── __init__.py
│ │ ├── module1.py
│ │ └── module2.py
│ └── setup.py
现在在您的包中使用常规导入,例如:
# in module2.py
from module1 import class1
这适用于python 2和3。
答案 3 :(得分:33)
我遇到了这个问题。黑客解决方法是通过if / else块导入,如下所示:
#!/usr/bin/env python3
#myothermodule
if __name__ == '__main__':
from mymodule import as_int
else:
from .mymodule import as_int
# Exported function
def add(a, b):
return as_int(a) + as_int(b)
# Test function for module
def _test():
assert add('1', '1') == 2
if __name__ == '__main__':
_test()
答案 4 :(得分:9)
对于PyCharm用户:
我也得到了from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
,因为我添加了ImportError: attempted relative import with no known parent package
表示法来使PyCharm解析错误静音。 PyCharm错误地报告无法找到:
.
如果将其更改为:
lib.thing import function
它使错误消失,但是随后您得到前面提到的.lib.thing import function
。只需忽略PyCharm的解析器即可。这是错误的,尽管说了什么,代码仍然可以正常运行。
答案 5 :(得分:4)
我的样板,用于在 module
可独立运行的情况下制作具有相对导入的 package
。
package/module.py
## Standalone boilerplate before relative imports
if __package__ is None:
DIR = Path(__file__).resolve().parent
sys.path.insert(0, str(DIR.parent))
__package__ = DIR.name
from . import variable_in__init__py
from . import other_module_in_package
...
现在您可以以任何方式使用您的模块:
python -m package.module
python -c 'from package import module'
python package/module.py
#!/bin/env python
) 只需:package/module.py
注意!如果您的 sys.path.append
与您的 {{1} 同名,使用 sys.path.insert
而不是 module
会给您带来难以追踪的错误}.例如。 package
当然,如果您有来自包层次结构中更高级别的相对导入,这还不够,但在大多数情况下,这没问题。
答案 6 :(得分:4)
希望这对那里的人有价值 - 我经历了六个stackoverflow帖子,试图找出类似于上面发布的相关导入。我按照建议设置了所有内容,但我仍在按ModuleNotFoundError: No module named 'my_module_name'
由于我只是在本地开发并玩游戏,所以我没有创建/运行setup.py
文件。我也没有显然设置PYTHONPATH
。
我意识到当我运行我的代码时,就像测试与模块在同一目录中一样,我找不到我的模块:
$ python3 test/my_module/module_test.py 2.4.0
Traceback (most recent call last):
File "test/my_module/module_test.py", line 6, in <module>
from my_module.module import *
ModuleNotFoundError: No module named 'my_module'
但是,当我明确指定路径开始工作时:
$ PYTHONPATH=. python3 test/my_module/module_test.py 2.4.0
...........
----------------------------------------------------------------------
Ran 11 tests in 0.001s
OK
所以,如果有人尝试了一些建议,请相信他们的代码结构正确并且仍然发现自己处于类似的情况,如果你没有将当前目录导出到PYTHONPATH,请尝试以下任一方法:
$ PYTHONPATH=. python3 test/my_module/module_test.py
PYTHONPATH=.
,请创建一个包含以下内容的setup.py
文件,然后运行python setup.py development
将包添加到路径中:# setup.py from setuptools import setup, find_packages setup( name='sample', packages=find_packages() )
答案 7 :(得分:3)
我收到此 ImportError:尝试进行相对导入,但没有已知的父包
在我的程序中,我使用当前路径中的文件导入其功能。
from .filename import function
然后我用包名称修改了当前路径(Dot)。哪个解决了我的问题。
from package_name.filename import function
希望以上答案对您有所帮助。
答案 8 :(得分:2)
为了避免这个问题,我设计了一个repackage包的解决方案,这对我来说已经有一段时间了。它将上层目录添加到lib路径:
import repackage
repackage.up()
from mypackage.mymodule import myfunction
重新打包可以使用智能策略(检查调用堆栈)使相对导入适用于各种情况。
答案 9 :(得分:2)
如果以上方法都不适合您,则可以显式指定模块。
目录:
├── Project
│ ├── Dir
│ │ ├── __init__.py
│ │ ├── module.py
│ │ └── standalone.py
解决方案:
#in standalone.py
from Project.Dir.module import ...
module-要导入的模块
答案 10 :(得分:1)
我需要从主项目目录运行python3才能使它工作。
例如,如果项目具有以下结构:
project_demo/
├── main.py
├── some_package/
│ ├── __init__.py
│ └── project_configs.py
└── test/
└── test_project_configs.py
我将在文件夹 project_demo / 中运行python3,然后执行
from some_package import project_configs
答案 11 :(得分:1)
如果两个包都在你的导入路径(sys.path)中,并且你想要的模块/类在example / example.py中,那么要访问没有相对导入的类,请尝试:
from example.example import fkt
答案 12 :(得分:0)
我认为最好的解决方案是为您的模块创建一个软件包: Here提供了更多有关操作方法的信息。
一旦有了软件包,您就不必担心相对导入,那么您就可以进行绝对导入。
答案 13 :(得分:0)
我有一个类似的问题:我需要一个Linux服务和cgi插件,它们使用公共常量进行协作。做到这一点的“自然”方法是将它们放置在软件包的 init .py中,但是我无法使用-m参数启动cgi插件。
我的最终解决方案与上面的解决方案2类似:
import sys
import pathlib as p
import importlib
pp = p.Path(sys.argv[0])
pack = pp.resolve().parent
pkg = importlib.import_module('__init__', package=str(pack))
缺点是必须在常量(或通用函数)前加上pkg:
print(pkg.Glob)
答案 14 :(得分:0)
将要导入的文件移动到外部目录会有所帮助。
当主文件在其自己的目录中创建其他任何文件时,此功能特别有用。
例如:
之前:
项目
| --- dir1
| ------- main.py
| ------- module1.py
之后:
项目
| --- module1.py
| --- dir1
| ------- main.py
答案 15 :(得分:0)
TLDR;通过在您的python脚本的入口点中添加以下内容,将脚本路径添加到系统路径中。
import os.path
import sys
PACKAGE_PARENT = '..'
SCRIPT_DIR = os.path.dirname(os.path.realpath(os.path.join(os.getcwd(), os.path.expanduser(__file__))))
sys.path.append(os.path.normpath(os.path.join(SCRIPT_DIR, PACKAGE_PARENT)))
就这样,现在您可以在PyCharma中以及在Terminal中运行项目!
答案 16 :(得分:0)
我尝试以上所有方法均无济于事,只是意识到我错误地在软件包名称中使用了-
。
简而言之,-
所在的目录中没有__init__.py
。发现这种烦恼之后,我再也不会感到高兴。
答案 17 :(得分:0)
我在使用 Django 时经常遇到这种情况,因为很多功能是从 manage.py
脚本执行的,但我也希望我的一些模块也可以直接作为脚本运行(理想情况下,您会使它们成为 manage.py
指令,但我们还没有)。
这是这样一个项目的模型;
├── dj_app
│ ├── models.py
│ ├── ops
│ │ ├── bar.py
│ │ └── foo.py
│ ├── script.py
│ ├── tests.py
│ ├── utils.py
│ └── views.py
└── manage.py
这里的重要部分是 manage.py
、dj_app/script.py
和 dj_app/tests.py
。我们还有子模块 dj_app/ops/bar.py
和 dj_app/ops/foo.py
,其中包含我们希望在整个项目中使用的更多项目。
问题的根源通常是希望您的 dj_app/script.py
脚本方法在 dj_app/tests.py
中有测试用例,这些用例在您运行 manage.py test
时被调用。
这就是我设置项目及其import
的方式;
# dj_app/ops/foo.py
# Foo operation methods and classes
foo_val = "foo123"
.
# dj_app/ops/bar.py
# Bar operations methods and classes
bar_val = "bar123"
.
# dj_app/script.py
# script to run app methods from CLI
# if run directly from command line
if __name__ == '__main__':
from ops.bar import bar_val
from ops.foo import foo_val
# otherwise
else:
from .ops.bar import bar_val
from .ops.foo import foo_val
def script_method1():
print("this is script_method1")
print("bar_val: {}".format(bar_val))
print("foo_val: {}".format(foo_val))
if __name__ == '__main__':
print("running from the script")
script_method1()
.
# dj_app/tests.py
# test cases for the app
# do not run this directly from CLI or the imports will break
from .script import script_method1
from .ops.bar import bar_val
from .ops.foo import foo_val
def main():
print("Running the test case")
print("testing script method")
script_method1()
if __name__ == '__main__':
print("running tests from command line")
main()
.
# manage.py
# just run the test cases for this example
import dj_app.tests
dj_app.tests.main()
.
从 manage.py
运行测试用例;
$ python3 manage.py
Running the test case
testing script method
this is script_method1
bar_val: bar123
foo_val: foo123
自行运行脚本;
$ python3 dj_app/script.py
running from the script
this is script_method1
bar_val: bar123
foo_val: foo123
请注意,如果您尝试直接运行 test.py
,则会出现错误,因此请不要这样做;
$ python3 dj_app/tests.py
Traceback (most recent call last):
File "dj_app/tests.py", line 5, in <module>
from .script import script_method1
ModuleNotFoundError: No module named '__main__.script'; '__main__' is not a package
如果我遇到更复杂的导入情况,我通常最终会实施类似的方法来破解它;
import os
import sys
THIS_DIR = os.path.dirname(os.path.realpath(__file__))
sys.path.insert(0, THIS_DIR)
from script import script_method1
sys.path.pop(0)
答案 18 :(得分:0)
这是我的项目结构
├── folder
| |
│ ├── moduleA.py
| | |
| | └--function1()
| | └~~ uses function2()
| |
│ └── moduleB.py
| |
| └--function2()
|
└── main.py
└~~ uses function1()
这里我的 moduleA
导入 moduleB
和 main
导入 moduleA
我在 moduleA
中添加了以下代码段以导入 moduleB
try:
from .moduleB import function2
except:
from moduleB import function2
现在我可以单独执行 main.py
和 moduleA.py
这是一个解决方案吗?
答案 19 :(得分:0)
pathlib
库,适用于未定义 __file__
的 Jupyter 笔记本:您想导入在 my_function
下定义的 ../my_Folder_where_the_package_lives/my_package.py
尊重您编写代码的位置。
然后做:
import os
import sys
import pathlib
PACKAGE_PARENT = Path.cwd().parent
SCRIPT_DIR = PACKAGE_PARENT / "my_Folder_where_the_package_lives"
sys.path.append(str(SCRIPT_DIR))
from my_package import my_function
答案 20 :(得分:-3)
我遇到了类似的问题,并通过在工作目录中创建一个符号链接来解决它:
ln -s ../../../my_package my_package
然后照常导入:
import my_package
我知道这更像是“Linux”解决方案而不是“Python”解决方案。但这仍然是一种有效的方法。