Dmi*_*mov 9 python import module package sys.path
Here is my directory structure:
/home/dmugtasimov/tmp/name-res
root
tests
__init__.py
test_1.py
__init__.py
classes.py
extra.py
root.py
Run Code Online (Sandbox Code Playgroud)
File contents: root/tests/_init_.py
import os, sys
ROOT_DIRECTORY = os.path.abspath(os.path.join(os.path.dirname(__file__),
'../..'))
if not sys.path or ROOT_DIRECTORY not in sys.path:
sys.path.insert(0, ROOT_DIRECTORY)
# These imports are required for unittest to find test modules in package properly
from root.tests import test_1
Run Code Online (Sandbox Code Playgroud)
root/tests/test_1.py
import unittest
from root.classes import Class1
class Tests(unittest.TestCase):
pass
Run Code Online (Sandbox Code Playgroud)
root/_init_.py - empty
root/classes.py
import os, sys
ROOT_DIRECTORY = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
if not sys.path or ROOT_DIRECTORY not in sys.path:
sys.path.insert(0, ROOT_DIRECTORY)
print 'sys.path:', sys.path
print 'BEFORE: import root.extra'
import root.extra
print 'AFTER: import root.extra'
class Class1(object):
pass
class Class2(object):
pass
Run Code Online (Sandbox Code Playgroud)
root/extra.py
class Class3(object):
pass
Run Code Online (Sandbox Code Playgroud)
root/root.py
import os
import sys
ROOT_DIRECTORY = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
if not sys.path or ROOT_DIRECTORY not in sys.path:
sys.path.insert(0, ROOT_DIRECTORY)
from classes import Class2
Run Code Online (Sandbox Code Playgroud)
I get following output:
$ python -m unittest tests.test_1
sys.path: ['/home/dmugtasimov/tmp/name-res', '', '/usr/local/lib/python2.7/dist-packages/tornado-2.3-py2.7.egg', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/local/lib/python2.7/dist-packages/setuptools-0.6c11-py2.7.egg-info', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/python2.7/dist-packages/ubuntu-sso-client']
BEFORE: import root.extra
sys.path: ['/home/dmugtasimov/tmp/name-res', '', '/usr/local/lib/python2.7/dist-packages/tornado-2.3-py2.7.egg', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/local/lib/python2.7/dist-packages/setuptools-0.6c11-py2.7.egg-info', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/python2.7/dist-packages/ubuntu-sso-client']
BEFORE: import root.extra
Traceback (most recent call last):
File "/usr/lib/python2.7/runpy.py", line 162, in _run_module_as_main
"__main__", fname, loader, pkg_name)
File "/usr/lib/python2.7/runpy.py", line 72, in _run_code
exec code in run_globals
File "/usr/lib/python2.7/unittest/__main__.py", line 12, in <module>
main(module=None)
File "/usr/lib/python2.7/unittest/main.py", line 94, in __init__
self.parseArgs(argv)
File "/usr/lib/python2.7/unittest/main.py", line 149, in parseArgs
self.createTests()
File "/usr/lib/python2.7/unittest/main.py", line 158, in createTests
self.module)
File "/usr/lib/python2.7/unittest/loader.py", line 128, in loadTestsFromNames
suites = [self.loadTestsFromName(name, module) for name in names]
File "/usr/lib/python2.7/unittest/loader.py", line 91, in loadTestsFromName
module = __import__('.'.join(parts_copy))
File "tests/__init__.py", line 9, in <module>
from root.tests import test_1
File "/home/dmugtasimov/tmp/name-res/root/tests/__init__.py", line 9, in <module>
from root.tests import test_1
File "/home/dmugtasimov/tmp/name-res/root/tests/test_1.py", line 3, in <module>
from root.classes import Class1
File "/home/dmugtasimov/tmp/name-res/root/classes.py", line 9, in <module>
import root.extra
File "/home/dmugtasimov/tmp/name-res/root/root.py", line 6, in <module>
from classes import Class2
ImportError: cannot import name Class2
Run Code Online (Sandbox Code Playgroud)
It turns out the problem is the order used by python interpreter to search for package or module:
$ python -vv -m unittest tests.test_1
…skipped...
import root.classes # precompiled from /home/dmugtasimov/tmp/name-res/root/classes.pyc
sys.path: ['/home/dmugtasimov/tmp/name-res', '', '/usr/local/lib/python2.7/dist-packages/tornado-2.3-py2.7.egg', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/local/lib/python2.7/dist-packages/setuptools-0.6c11-py2.7.egg-info', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/python2.7/dist-packages/ubuntu-sso-client']
BEFORE: import root.extra
# trying /home/dmugtasimov/tmp/name-res/root/root.so
# trying /home/dmugtasimov/tmp/name-res/root/rootmodule.so
# trying /home/dmugtasimov/tmp/name-res/root/root.py
# /home/dmugtasimov/tmp/name-res/root/root.pyc matches /home/dmugtasimov/tmp/name-res/root/root.py
…skipped...
Run Code Online (Sandbox Code Playgroud)
According to python documentation http://docs.python.org/2/tutorial/modules.html#the-module-search-path: "When a module named spam is imported, the interpreter first searches for a built-in module with that name. If not found, it then searches for a file named spam.py in a list of directories given by the variable sys.path."
This means that python should look at sys.path index 0 entry, get path '/home/dmugtasimov/tmp/name-res' and find package named root and then search module named extra in this package. But instead it searches in /home/dmugtasimov/tmp/name-res/root/ directory for module root and then tries to find something named extra in it. What does it happen? Does not it contradict official documentation? Or are rules for searching packages different than for modules? If so, are these rules covered somewhere in documentation?
I put it here for better formatting.
For futher investigation do the following:
Run Code Online (Sandbox Code Playgroud)# trying /home/dmugtasimov/tmp/name-res/root/root.so # trying /home/dmugtasimov/tmp/name-res/root/rootmodule.so # trying /home/dmugtasimov/tmp/name-res/root/root.py # trying /home/dmugtasimov/tmp/name-res/root/root.pyc # trying /home/dmugtasimov/tmp/name-res/root/extra.so # trying /home/dmugtasimov/tmp/name-res/root/extramodule.so # trying /home/dmugtasimov/tmp/name-res/root/extra.py
It appears python disregard sys.path only for initial 4 tries.
Simplified version:
/home/dmugtasimov/tmp/name-res3/xyz
__init__.py
a.py
b.py
t.py
xyz.py
Run Code Online (Sandbox Code Playgroud)
文件init .py,b.py和xyz.py为空
文件a.py:
import os, sys
ROOT_DIRECTORY = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
if not sys.path or ROOT_DIRECTORY not in sys.path:
print 'sys.path is modified in a.py'
sys.path.insert(0, ROOT_DIRECTORY)
else:
print 'sys.path is NOT modified in a.py'
print 'sys.path:', sys.path
print 'BEFORE import xyz.b'
import xyz.b
print 'AFTER import xyz.b'
Run Code Online (Sandbox Code Playgroud)
文件t.py:
import os, sys
ROOT_DIRECTORY = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
if not sys.path or ROOT_DIRECTORY not in sys.path:
print 'sys.path is modified in t.py'
sys.path.insert(0, ROOT_DIRECTORY)
else:
print 'sys.path is NOT modified in t.py'
import xyz.a
Run Code Online (Sandbox Code Playgroud)
跑:
python a.py
Run Code Online (Sandbox Code Playgroud)
输出:
Run Code Online (Sandbox Code Playgroud)sys.path is modified in a.py sys.path: ['/home/dmugtasimov/tmp/name-res3', '/home/dmugtasimov/tmp/name-res3/xyz', '/usr/local/lib/python2.7/dist-packages/tornado-2.3-py2.7.egg', '/home/dmugtasimov/tmp/name-res3/xyz', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/local/lib/python2.7/dist-packages/setuptools-0.6c11-py2.7.egg-info', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/python2.7/dist-packages/ubuntu-sso-client'] BEFORE import xyz.b AFTER import xyz.b
跑:
python -vv a.py
Run Code Online (Sandbox Code Playgroud)
输出:
Run Code Online (Sandbox Code Playgroud)import xyz # directory /home/dmugtasimov/tmp/name-res3/xyz # trying /home/dmugtasimov/tmp/name-res3/xyz/__init__.so # trying /home/dmugtasimov/tmp/name-res3/xyz/__init__module.so # trying /home/dmugtasimov/tmp/name-res3/xyz/__init__.py # /home/dmugtasimov/tmp/name-res3/xyz/__init__.pyc matches /home/dmugtasimov/tmp/name-res3/xyz/__init__.py import xyz # precompiled from /home/dmugtasimov/tmp/name-res3/xyz/__init__.pyc # trying /home/dmugtasimov/tmp/name-res3/xyz/b.so # trying /home/dmugtasimov/tmp/name-res3/xyz/bmodule.so # trying /home/dmugtasimov/tmp/name-res3/xyz/b.py # /home/dmugtasimov/tmp/name-res3/xyz/b.pyc matches /home/dmugtasimov/tmp/name-res3/xyz/b.py import xyz.b # precompiled from /home/dmugtasimov/tmp/name-res3/xyz/b.pyc
跑:
python t.py
Run Code Online (Sandbox Code Playgroud)
输出:
Run Code Online (Sandbox Code Playgroud)sys.path is modified in t.py sys.path is NOT modified in a.py sys.path: ['/home/dmugtasimov/tmp/name-res3', '/home/dmugtasimov/tmp/name-res3/xyz', '/usr/local/lib/python2.7/dist-packages/tornado-2.3-py2.7.egg', '/home/dmugtasimov/tmp/name-res3/xyz', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/local/lib/python2.7/dist-packages/setuptools-0.6c11-py2.7.egg-info', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/python2.7/dist-packages/ubuntu-sso-client'] BEFORE import xyz.b Traceback (most recent call last): File "t.py", line 9, in <module> import xyz.a File "/home/dmugtasimov/tmp/name-res3/xyz/a.py", line 11, in <module> import xyz.b ImportError: No module named b
跑:
python -vv t.py
Run Code Online (Sandbox Code Playgroud)
输出:
Run Code Online (Sandbox Code Playgroud)import xyz # directory /home/dmugtasimov/tmp/name-res3/xyz # trying /home/dmugtasimov/tmp/name-res3/xyz/__init__.so # trying /home/dmugtasimov/tmp/name-res3/xyz/__init__module.so # trying /home/dmugtasimov/tmp/name-res3/xyz/__init__.py # /home/dmugtasimov/tmp/name-res3/xyz/__init__.pyc matches /home/dmugtasimov/tmp/name-res3/xyz/__init__.py import xyz # precompiled from /home/dmugtasimov/tmp/name-res3/xyz/__init__.pyc # trying /home/dmugtasimov/tmp/name-res3/xyz/a.so # trying /home/dmugtasimov/tmp/name-res3/xyz/amodule.so # trying /home/dmugtasimov/tmp/name-res3/xyz/a.py # /home/dmugtasimov/tmp/name-res3/xyz/a.pyc matches /home/dmugtasimov/tmp/name-res3/xyz/a.py import xyz.a # precompiled from /home/dmugtasimov/tmp/name-res3/xyz/a.pyc # trying /home/dmugtasimov/tmp/name-res3/xyz/os.so # trying /home/dmugtasimov/tmp/name-res3/xyz/osmodule.so # trying /home/dmugtasimov/tmp/name-res3/xyz/os.py # trying /home/dmugtasimov/tmp/name-res3/xyz/os.pyc # trying /home/dmugtasimov/tmp/name-res3/xyz/sys.so # trying /home/dmugtasimov/tmp/name-res3/xyz/sysmodule.so # trying /home/dmugtasimov/tmp/name-res3/xyz/sys.py # trying /home/dmugtasimov/tmp/name-res3/xyz/sys.pyc # trying /home/dmugtasimov/tmp/name-res3/xyz/xyz.so # trying /home/dmugtasimov/tmp/name-res3/xyz/xyzmodule.so # trying /home/dmugtasimov/tmp/name-res3/xyz/xyz.py # /home/dmugtasimov/tmp/name-res3/xyz/xyz.pyc matches /home/dmugtasimov/tmp/name-res3/xyz/xyz.py import xyz.xyz # precompiled from /home/dmugtasimov/tmp/name-res3/xyz/xyz.pyc # clear[2] __file__ # clear[2] __package__ # clear[2] sys # clear[2] ROOT_DIRECTORY # clear[2] __name__ # clear[2] os sys.path is modified in t.py sys.path is NOT modified in a.py sys.path: ['/home/dmugtasimov/tmp/name-res3', '/home/dmugtasimov/tmp/name-res3/xyz', '/usr/local/lib/python2.7/dist-packages/tornado-2.3-py2.7.egg', '/home/dmugtasimov/tmp/name-res3/xyz', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/local/lib/python2.7/dist-packages/setuptools-0.6c11-py2.7.egg-info', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/python2.7/dist-packages/ubuntu-sso-client'] BEFORE import xyz.b Traceback (most recent call last): File "t.py", line 9, in <module> import xyz.a File "/home/dmugtasimov/tmp/name-res3/xyz/a.py", line 11, in <module> import xyz.b ImportError: No module named b
如您所见,sys.path对于两种情况都是相同的:
sys.path: ['/home/dmugtasimov/tmp/name-res3', '/home/dmugtasimov/tmp/name-res3/xyz', '/usr/local/lib/python2.7/dist-packages/tornado-2.3-py2.7.egg', '/home/dmugtasimov/tmp/name-res3/xyz', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/local/lib/python2.7/dist-packages/setuptools-0.6c11-py2.7.egg-info', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/python2.7/dist-packages/ubuntu-sso-client']
Run Code Online (Sandbox Code Playgroud)
但行为是不同的.对于a.py python首先搜索包xyz,并在其中搜索模块b:
Run Code Online (Sandbox Code Playgroud)import xyz # directory /home/dmugtasimov/tmp/name-res3/xyz # trying /home/dmugtasimov/tmp/name-res3/xyz/__init__.so # trying /home/dmugtasimov/tmp/name-res3/xyz/__init__module.so # trying /home/dmugtasimov/tmp/name-res3/xyz/__init__.py # /home/dmugtasimov/tmp/name-res3/xyz/__init__.pyc matches /home/dmugtasimov/tmp/name-res3/xyz/__init__.py import xyz # precompiled from /home/dmugtasimov/tmp/name-res3/xyz/__init__.pyc # trying /home/dmugtasimov/tmp/name-res3/xyz/b.so # trying /home/dmugtasimov/tmp/name-res3/xyz/bmodule.so # trying /home/dmugtasimov/tmp/name-res3/xyz/b.py # /home/dmugtasimov/tmp/name-res3/xyz/b.pyc matches /home/dmugtasimov/tmp/name-res3/xyz/b.py import xyz.b # precompiled from /home/dmugtasimov/tmp/name-res3/xyz/b.pyc
换一种说法:
对于t.py,它在与a.py本身相同的目录中搜索moduel xyz,然后在模块xyz中找不到模块b:
Run Code Online (Sandbox Code Playgroud)# trying /home/dmugtasimov/tmp/name-res3/xyz/xyz.so # trying /home/dmugtasimov/tmp/name-res3/xyz/xyzmodule.so # trying /home/dmugtasimov/tmp/name-res3/xyz/xyz.py # /home/dmugtasimov/tmp/name-res3/xyz/xyz.pyc matches /home/dmugtasimov/tmp/name-res3/xyz/xyz.py import xyz.xyz # precompiled from /home/dmugtasimov/tmp/name-res3/xyz/xyz.pyc
换一种说法:
所以看起来"import xyz.b"bahaves会有所不同,具体取决于a.py最初如何作为脚本加载或从另一个模块导入.
I filed proposal for documentation fix: http://bugs.python.org/issue16891
The reason for behavior described in UPDATE 2 is completely clear for me now.
http://docs.python.org/2/tutorial/modules.html#intra-package-references
6.4.2. Intra-package References
The submodules often need to refer to each other. For example, the surround module might use the echo module. In fact, such references are so common that the import statement first looks in the containing package before looking in the standard module search path. Thus, the surround module can simply use import echo or from echo import echofilter. If the imported module is not found in the current package (the package of which the current module is a submodule), the import statement looks for a top-level module with the given name.
For "python a.py" "a" is not considered as module in package, but for "python t.py" "a" is considered as a module in package "xyz". Therefore in first case it searches according sys.path, but in the second case it searches inside the same package (namely "xyz") for module named "xyz" (in other words "xyz.xyz")
You can easily see if change a.py like this:
File a.py:
import os, sys
ROOT_DIRECTORY = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
if not sys.path or ROOT_DIRECTORY not in sys.path:
print 'sys.path is modified in a.py'
sys.path.insert(0, ROOT_DIRECTORY)
else:
print 'sys.path is NOT modified in a.py'
print 'sys.path:', sys.path
print '__package__', __package__
print 'BEFORE import xyz.b'
import xyz.b
print 'AFTER import xyz.b'
Run Code Online (Sandbox Code Playgroud)
My output was:
Run Code Online (Sandbox Code Playgroud)~/tmp/name-res3/xyz $ python a.py sys.path is modified in a.py sys.path: ['/home/dmugtasimov/tmp/name-res3', '/home/dmugtasimov/tmp/name-res3/xyz', '/usr/local/lib/python2.7/dist-packages/tornado-2.3-py2.7.egg', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/local/lib/python2.7/dist-packages/setuptools-0.6c11-py2.7.egg-info', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/python2.7/dist-packages/ubuntu-sso-client'] __package__ None BEFORE import xyz.b AFTER import xyz.b ~/tmp/name-res3/xyz $ python t.py sys.path is modified in t.py sys.path is NOT modified in a.py sys.path: ['/home/dmugtasimov/tmp/name-res3', '/home/dmugtasimov/tmp/name-res3/xyz', '/usr/local/lib/python2.7/dist-packages/tornado-2.3-py2.7.egg', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/local/lib/python2.7/dist-packages/setuptools-0.6c11-py2.7.egg-info', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/python2.7/dist-packages/ubuntu-sso-client'] __package__ xyz BEFORE import xyz.b Traceback (most recent call last): File "t.py", line 9, in <module> import xyz.a File "/home/dmugtasimov/tmp/name-res3/xyz/a.py", line 12, in <module> import xyz.b ImportError: No module named b
Thanks to @J.F. Sebastian for pointing out the right place of documentation.
It seems that there is another issue. If interested, please, follow comments here: http://bugs.python.org/issue16891
我简化了问题中的示例来演示只有四种解决方案是可能的:
from . import some_module
或使用更多逗号from ..
from __future__ import absolute_import
(或使用Python 3)什么解决方案是最好的?这取决于个人对 Python 2 或 3 的偏好。只有最后一个对所有 Python 来说都是好的和通用的。这确实是一个有用的问题。
xyz/tests/__init__.py:import xyz.tests.t
xyz/测试/t.py:
import sys
print('sys.path = %s' % sys.path) # see that the parent of "xyz" is on sys.path
print("importing xyz.tests")
import xyz.a
Run Code Online (Sandbox Code Playgroud)
xyz/a.py:
# solution A: absolute_import by __future__ (or use Python 3)
#from __future__ import absolute_import
print("importing xyz.a")
# solution B: explicit relative import
#from . import b # and remove "import xyz.b"
# solution C: relative import (not recommended)
#import b # and remove "import xyz.b"
import xyz.b
Run Code Online (Sandbox Code Playgroud)
xyz/b.py:print("imported xyz.b")
xyz/xyz.py:print("Imported xyz.xyz !!!")
xyz/__init__.py:空文件
一切可能的失败,例如
parent_of_xyz=... # The parent directory of "xyz" - absolute path
cd $parent_of_xyz
python -m xyz.tests.t
PYTHONPATH=$parent_of_xyz/xyz python -m unittest tests
PYTHONPATH=$parent_of_xyz python xyz/tests/t.py
Run Code Online (Sandbox Code Playgroud)
与类似的消息
Imported xyz.xyz !!!
...
ImportError...
Run Code Online (Sandbox Code Playgroud)
如果应用任何解决方案(未注释),则所有三个示例都有效。
不使用任何子目录可以更加简化。
编辑: 我昨天尝试了很多测试,但我从不同版本编写的内容不一致。抱歉,答案无法重现。现在它已经修复了。