为什么我可以在没有__init__.py的情况下成功导入?

时间:2016-06-22 17:39:06

标签: python import

__init__.py的用途究竟是什么?是的,我知道这个文件将目录放入一个可导入的包中。但是,请考虑以下示例:

project/
   foo/
      __init__.py
      a.py
      bar/
         b.py

如果我要将a导入b,我必须添加以下声明:

sys.path.append('/path_to_foo')
import foo.a

无论有没有__init__.py,这都会成功运行。但是,如果没有sys.path.append语句,则会出现“无模块”错误,包含或不包含__init__.py。这使得系统路径看起来很重要,__init__.py没有任何影响。

为什么没有__init__.py此导入工作?

4 个答案:

答案 0 :(得分:9)

__init__.py与Python是否可以找到您的包无关。您的代码运行方式默认情况下您的软件包不在搜索路径上,但如果您以不同的方式运行它或以不同的方式配置PYTHONPATH,则sys.path.append将不再必需

以前需要

__init__.py创建一个包,在大多数情况下,你仍然应该提供它。但是,从Python 3.3开始,没有__init__.py的文件夹可以被视为implicit namespace package的一部分,这是一个跨多个目录拆分包的功能。

  

在导入处理期间,导入机器将继续   迭代父路径中的每个目录,就像在Python中一样   3.2。在寻找名为“foo”的模块或包时,对于父路径中的每个目录:

     
      
  • 如果找到<directory>/foo/__init__.py,则会导入并返回常规包。
  •   
  • 如果没有,但找到<directory>/foo.{py,pyc,so,pyd},则会导入并返回模块。扩展名的确切列表因平台而异   以及是否指定了-O标志。这里的清单是   代表。
  •   
  • 如果没有,但找到<directory>/foo并且是一个目录,则会记录,并继续扫描父目录中的下一个目录   路径。
  •   
  • 否则,扫描将继续执行父路径中的下一个目录。
  •   
     

如果扫描完成而未返回模块或包,则 at   记录了至少一个目录,然后创建了一个命名空间包。

答案 1 :(得分:1)

  

如果我要将a导入b,我必须添加以下语句:

没有!你只想说:import foo.a。所有这些都是在您使用python -m main.module一次运行整个包时提供的,其中main.module是整个应用程序的入口点。它导入所有其他模块,导入更多模块的模块将尝试从该项目的根目录中查找它们。例如,foo.bar.c将导入为foo.bar.b

  

然后似乎只有系统路径很重要, init .py没有任何影响。

只有在从项目中不存在的位置导入模块或python查找库的位置时,才需要修改sys.path__init__.py不仅使文件夹看起来像一个包,它还会做一些更多的事情,例如将对象“导出”到外部世界(__all__

答案 2 :(得分:0)

如果确实出于某种原因想要避免__init__.py,则不要sys.path。而是创建一个模块对象并将其__path__设置为目录列表。

答案 3 :(得分:0)

当你导入它必须的东西时:

  1. 检索已加载的模块或
  2. 加载导入的模块
  3. 当您执行import foo并且python在foo的文件夹中找到名为sys.path的文件夹时,它会在该文件夹中查找__init__.py被视为顶级模块。

    (请注意,如果包裹不在sys.path上,那么您需要append它的位置才能导入它。)

    如果不存在,它将在__init__.pyc文件夹中查找__pycache__版本,如果还缺少,则该文件夹foo不被视为可加载的python包。如果找不到foo的其他选项,则会引发ImportError

    如果您尝试删除__init__.pyc文件,您将看到确实需要包的初始化程序脚本。