如果模块的名称相同,为什么python会查找模块而不是包?

如果模块的名称相同,为什么python会查找模块而不是包?,python,import,module,package,sys.path,Python,Import,Module,Package,Sys.path,以下是我的目录结构: /home/dmugtasimov/tmp/name-res root tests __init__.py test_1.py __init__.py classes.py extra.py root.py 文件内容: root/tests/_init.py root/tests/test_1.py root/\u init.py-空

以下是我的目录结构:

/home/dmugtasimov/tmp/name-res
    root
        tests
            __init__.py
            test_1.py
        __init__.py
        classes.py
        extra.py
        root.py
文件内容: root/tests/_init.py

root/tests/test_1.py

root/\u init.py-空
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
class Class3(object):
    pass
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
root/extra.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
class Class3(object):
    pass
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
root/root.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
class Class3(object):
    pass
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

我得到以下输出:

$ 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

根据python文档: 导入名为spam的模块时,解释器首先搜索具有该名称的内置模块。如果未找到,则在变量sys.path给定的目录列表中搜索名为spam.py的文件

这意味着python应该查看sys.path索引0条目,获取路径“/home/dmugtasimov/tmp/name res”,找到名为root的包,然后在此包中搜索名为extra的模块。但它在/home/dmugtasimov/tmp/name res/root/目录中搜索模块根,然后尝试在其中查找名为extra的内容。发生了什么事?这与官方文件不矛盾吗?或者搜索包的规则与模块的规则不同?如果是,这些规则是否包含在文档中

更新 我把它放在这里是为了更好地格式化。

为了进一步调查,请执行以下操作:

  • 删除root.pyc
  • 将root.py重命名为root2.py
  • 运行python-vv-m unittest tests.test_1
  • 在最初的4次尝试中,python似乎只忽略sys.path

    更新2 简化版:

    /home/dmugtasimov/tmp/name-res3/xyz
        __init__.py
        a.py
        b.py
        t.py
        xyz.py
    
    文件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'
    
    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'
    
    文件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
    
    运行:

    输出:

    运行:

    输出:

    运行:

    输出:

    输出:

    但行为是不同的。对于a.py,python首先搜索包xyz,然后搜索其中的模块b:

    换言之:

  • 在目录sys.path[0]->中搜索程序包xyz
  • 在程序包xyz中搜索模块b->找到
  • 继续执行
  • 对于t.py,它在与a.py自身相同的目录中搜索moduel xyz,然后在模块xyz中找不到模块b:

    换言之:

  • 在与a.py(或sys.path[1]?)相同的目录中搜索模块xyz->FOUND
  • 在模块xyz中搜索模块b->未找到
  • 强求恐怖
  • 因此,看起来“import xyz.b”的用法有所不同,这取决于a.py最初作为脚本加载或从另一个模块导入的方式

    更新3 我提交了文件修复方案:

    更新4 更新2中描述的行为的原因现在对我来说是完全清楚的

    6.4.2。包内引用

    子模块通常需要相互引用。例如 环绕声模块可能使用回声模块。事实上,这些参考资料 是如此常见,以至于import语句首先在包含 在标准模块搜索路径中查找之前打包。因此 环绕声模块可以简单地使用导入回波或从回波导入 回声过滤器。如果在当前包中找不到导入的模块 (当前模块是其子模块的包),导入 语句查找具有给定名称的顶级模块

    对于“python a.py”,a不被视为包中的模块,但是对于“python t.py”,a被视为包“xyz”中的模块。因此,在第一种情况下,它根据sys.path进行搜索,但在第二种情况下,它在同一个包(即“xyz”)内搜索名为“xyz”的模块(即“xyz.xyz”)

    您可以很容易地看到是否像这样更改a.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'
    
    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'
    
    我的产出是:

    ~/tmp/name-res3/xyz$python a.py
    sys.path在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包”,
    “/usr/local/lib/python2.7/dist-packages/setuptools-0.6c11-py2.7.egg-info”,
    “/usr/lib/python2.7/dist包”,
    “/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”]
    __包\无
    在导入xyz.b之前
    导入xyz.b后
    ~/tmp/name-res3/xyz$python t.py
    sys.path在t.py中修改
    sys.path未在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包”,
    “/usr/local/lib/python2.7/dist-packages/setuptools-0.6c11-py2.7.egg-info”,
    “/usr/lib/python2.7/dist包”,
    “/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”]
    __包_xyz
    在导入xyz.b之前
    回溯(最近一次呼叫最后一次):
    文件“t.py”,第9行,在
    导入xyz.a
    文件“/home/dmugtasimov/tmp/name-res3/xyz/a.py”,第12行,在
    导入xyz.b
    ImportError:没有名为b的模块
    
    感谢@J.F.Sebastian指出了正确的文档位置

    更新5
    似乎还有另一个问题。如果有兴趣,请按照此处的注释进行操作:

    请勿修改
    sys.path
    当同一模块以不同名称提供时,会导致问题。看

    在代码中使用绝对或显式相对导入,并从项目目录运行脚本。使用全名运行测试:

    $ python -munittest root.tests.test_1
    
    某些软件包在内部修改系统路径,例如。,
    python -vv t.py
    
    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: ['/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']
    
    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
    
    # 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 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'
    
    ~/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
    
    $ python -munittest root.tests.test_1
    
    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
    
    # 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
    
    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
    
    Imported xyz.xyz  !!!
    ...
    ImportError...