我使用的是Python 3.5.1。我阅读了文档和软件包部分:https://docs.python.org/3/tutorial/modules.html#packages

现在,我有以下结构:

/home/wujek/Playground/a/b/module.py

module.py:

class Foo:
    def __init__(self):
        print('initializing Foo')

现在,在/home/wujek/Playground中:

~/Playground $ python3
>>> import a.b.module
>>> a.b.module.Foo()
initializing Foo
<a.b.module.Foo object at 0x100a8f0b8>

类似地,现在在home, Playground的超级文件夹中:

~ $ PYTHONPATH=Playground python3
>>> import a.b.module
>>> a.b.module.Foo()
initializing Foo
<a.b.module.Foo object at 0x10a5fee10>

事实上,我可以做各种各样的事情:

~ $ PYTHONPATH=Playground python3
>>> import a
>>> import a.b
>>> import Playground.a.b

为什么会这样?我虽然需要__init__.py文件(空的会工作)在a和b模块.py导入时,Python路径指向游乐场文件夹?

这似乎是从Python 2.7开始改变的:

~ $ PYTHONPATH=Playground python
>>> import a
ImportError: No module named a
>>> import a.b
ImportError: No module named a.b
>>> import a.b.module
ImportError: No module named a.b.module

在~/Playground/a和~/Playground/a/b中都有__init__.py,它可以正常工作。


Python 3.3+具有隐式命名空间包,允许它在没有__init__.py文件的情况下创建包。

允许隐式命名空间包意味着可以完全放弃提供__init__.py文件的要求,并影响... .

__init__.py文件的旧方法仍然可以在Python 2中工作。


概述

@Mike的回答是正确的,但是太不精确了。确实,Python 3.3+支持隐式命名空间包,允许它在没有__init__.py文件的情况下创建包。这被称为命名空间包,与常规包相反,常规包有__init__.py文件(空或不空)。

但是,只有在需要时才应该创建命名空间包。对于大多数用例和开发人员来说,这并不适用,所以你应该坚持使用EMPTY __init__.py文件。

命名空间包用例

为了演示这两种类型的python包之间的区别,让我们看下面的例子:

google_pubsub/              <- Package 1
    google/                 <- Namespace package (there is no __init__.py)
        cloud/              <- Namespace package (there is no __init__.py)
            pubsub/         <- Regular package (with __init__.py)
                __init__.py <- Required to make the package a regular package
                foo.py

google_storage/             <- Package 2
    google/                 <- Namespace package (there is no __init__.py)
        cloud/              <- Namespace package (there is no __init__.py)
            storage/        <- Regular package (with __init__.py)
                __init__.py <- Required to make the package a regular package
                bar.py

Google_pubsub和google_storage是单独的包,但它们共享相同的名称空间谷歌/cloud。为了共享同一个命名空间,需要将公共路径下的每个目录都设置为命名空间包,即谷歌/和cloud/。这应该是创建名称空间包的唯一用例,否则就不需要它了。

谷歌和谷歌/cloud目录中没有__init__py文件,这一点很重要,这样两个目录都可以解释为命名空间包。在Python 3.3+中,sys. exe目录下的任意目录。名称与正在查找的包名称匹配的路径将被识别为该包的贡献模块和子包。因此,当您同时从google_pubsub和google_storage导入时,Python解释器将能够找到它们。

This is different from regular packages which are self-contained meaning all parts live in the same directory hierarchy. When importing a package and the Python interpreter encounters a subdirectory on the sys.path with an __init__.py file, then it will create a single directory package containing only modules from that directory, rather than finding all appropriately named subdirectories outside that directory. This is perfectly fine for packages that don't want to share a namespace. I highly recommend taking a look at Traps for the Unwary in Python’s Import System to get a better understanding of how Python importing behaves with regular and namespace package and what __init__.py traps to watch out for.

总结

Only skip __init__.py files if you want to create namespace packages. Only create namespace packages if you have different libraries that reside in different locations and you want them each to contribute a subpackage to the parent package, i.e. the namespace package. Keep on adding empty __init__.py to your directories because 99% of the time you just want to create regular packages. Also, Python tools out there such as mypy and pytest require empty __init__.py files to interpret the code structure accordingly. This can lead to weird errors if not done with care.

资源

我的回答只触及了常规包和名称空间包的工作方式的表面,因此请查看以下资源以获得更多信息:

PEP 420——隐式命名空间包 导入系统-常规包 导入系统-命名空间包 Python导入系统中的粗心陷阱


我想说,只有当你想要隐式的命名空间包时,才应该省略__init__.py。如果你不知道它的意思,你可能不想要它,因此即使在Python 3中你也应该继续使用__init__.py。


如果你的项目中有setup.py,并且在其中使用find_packages(),那么在每个目录中都必须有__init__.py文件,以便自动找到包。

只有包含__init__.py文件的包才会被识别

UPD:如果你想使用隐式命名空间包而不使用__init__.py,你只需要使用find_namespace_packages()

Docs


根据我的经验,即使使用python 3.3+,有时仍然需要一个空的__init__.py。一种情况是您希望将子文件夹引用为包。例如,当我运行python -m test。foo,它没有工作,直到我在测试文件夹下创建了一个空的__init__.py。我说的是最近的3.6.6版本。

除此之外,即使是出于与现有源代码或项目指南兼容的原因,在你的包文件夹中有一个空的__init__.py也是很好的。