Python 相对进口测试我的理智

Python relative imports testing my sanity

编辑:循环依赖问题是 from .. import auth 不起作用的原因,请参阅下面我自己的回答。为大家干杯。


我正在尝试 Python 中的相关导入,同时重新组织一些代码,本来应该花 10 分钟的事情现在花费了无数时间。这是我的文件结构:

project/
       /__init__.py
       /application.py
       /lib/
           /__init__.py
           /auth.py
           /utils.py
           /views/
                 /__init__.py
                 /login_view.py
                 /test.py

login_view.py我有:

from . import auth

产生:ImportError: cannot import name auth

然而这有效:

from ..auth import untoken, req_auth_or_error

我不明白:auth 只比 login_view 高一级,为什么是 .. 而不是 .

这也失败并出现同样的错误:

from .. import auth

test.py 中,我尝试了以下操作:

import types
from .. import *

def imports():
    for name, val in globals().items():
        if isinstance(val, types.ModuleType):
            print 'imported: ', val.__name__

imports()

这导致:

imported:  lib.utils
imported:  lib.views
imported:  types

...我不知道为什么 lib.auth 没有导入。我做错了什么?

我是运行这样的代码(绝对路径是/scratch/project/):

$ cd project/
$ ./application.py

在第一种情况下,from . import auth . 表示包 views。这就是为什么 Python 找不到它的原因。

但是,要避免这些问题,recommended 使用绝对导入而不是相对导入。

如果导入时再上一级目录怎么办?

from ... import auth

问题是循环依赖,而不是相对导入。

确实,需要 .. 从父目录加载模块。

然而在 auth.py 的某处我有一个 import views.login_view。我相信 login_view.py 中的 from .. import auth 试图在 auth 模块自行加载时导入它。这可能是 from ..auth import untoken 工作的原因,因为这些功能已经加载。

在 Python 中,导入中的一个点表示当前 ,而不是当前模块。

然后每增加一个点代表一个新的水平"parentness"(好吧这个词不存在但我认为它很好地表达了我的意思)。 所以你在第一个案例中注意到的行为是正常的。

对于你的第二种情况,我用这些内容复制了你的层次结构:

application.py

lib.views import login_view

print("oh yeah")

login_view.py

lib.views import login_view

print("oh my god")
项目目录中的

... 和 运行 application.py 不会导致任何错误。我得到以下输出:

$ python application.py 
oh my god
oh yeah

但是,在 application.py 中添加 from lib.views import test(使用与您提供的完全相同的 test.py 内容)会得到以下输出,显示某些 lib 模块是未加载:

$ python application.py 
oh my god
imported:  types
imported:  lib.views
imported:  lib.auth
oh yeah

缺少 lib.utils 模块 :/

我的想法是,由于 from .. import * 没有命名:没有包、没有模块、没有函数或变量或任何东西,import 语句只是简单地导入任何东西。显示的 modules/packages 只是之前加载的那个。

也就是说,有一种方法可以通过使用 __all__ 魔法变量来强制执行 "correct" 行为,它定义了在使用 splat 导入时加载哪些模块,在 lib/__init__.py 文件:

__all__ = ['auth', 'views', 'utils']

执行此操作后,运行 application.py 文件再次显示以下内容:

$ python application.py 
oh my god
imported:  types
imported:  lib.auth
imported:  lib.views
imported:  lib.utils
oh yeah