为什么我在每个级别都需要__init__.py?

时间:2017-07-04 17:55:06

标签: python python-import python-module

鉴于我有以下目录结构,.是当前工作目录

.
\---foo
    \---bar
        \---__init__.py
        \---baz.py

当我运行python -c "import foo.bar.baz"时,我得到了

Traceback (most recent call last):
  File "<string>", line 1
ImportError: No module named foo.bar.baz

如果我echo "" > foo/__init__.py,则上述命令有效。

我做错了什么或者我误解了__init__.py的观点?我认为这是为了阻止他们不应该存在的模块,例如一个名为string的目录,但是如果在我的示例中用foo替换string,我似乎被迫创建了永远不会被使用的模块,这样我就可以引用一个文件更深层次的。

更新

我正在使用一个为我生成__init__.py的构建系统,并且正在强制执行目录结构,虽然我可能会混淆层次结构,但我更愿意添加{{1}我自己。要稍微改变一下这个问题,为什么我需要在每个级别而不是在顶部的python包?您只能从python路径或python路径中的一系列包中导入模块吗?

1 个答案:

答案 0 :(得分:7)

是的,如果您希望将目录视为模块,则需要此文件。

  

需要__init__.py个文件才能使Python将目录视为包含包;这样做是为了防止具有通用名称的目录(例如字符串)无意中隐藏稍后在模块搜索路径上发生的有效模块。在最简单的情况下,__init__.py可以只是一个空文件,但它也可以执行包的初始化代码或设置__all__变量,稍后将对此进行描述。

https://docs.python.org/3/tutorial/modules.html#packages

我尝试创建非空__init__.py。您很有可能通过在第一级提供最有用的对象(类/函数)来记录模块,去除用户/开发人员的嵌套导入......实际上,尽可能简单地使用相反 - 让我们说 - java进口

问题更新后修改

默认导入器/查找器(检查sys.meta_path)是:

  1. BuiltinImporter - 搜索/加载内置模块
  2. FrozenImporter - 搜索/加载冻结的模块(例如* .pyc)
  3. PathFinder - 你感兴趣的那个,允许根据文件系统搜索/加载模块
  4. 第三个是__init__.py事物(实际上也是FrozenImporter)。

    PathFindersys.path(以及包中定义的__path__)的路径中搜索模块。该模块可以是独立的python文件(如果它位于搜索路径的根目录中),也可以是__init__.py的目录。

    参考你的例子:

    foo/
      bar/
        __init__.py
        baz.py
    
    • 如果您在_init__.py中创建foo/foo.bar.baz将可用(如您所说)。

    • 如果您将foo/添加到sys.path或通过PYTHONPATH=foo/传递,bar.baz将可用(请注意,如果没有父模块foo)。

    • 如果您编写自己的finder(和Loader),您可以加载任何您想要的文件,不管它在哪里。这给了你很大的力量。例如,查看stack-overflow-import,根据SO的搜索结果公开代码。