模块导入和Python中的__init__.py

问题描述 投票:28回答:5

我试图了解Python(v2.7)导入机制的最佳实践。我有一个项目已经开始增长一点,让我说我的代码组织如下:

foo/
    __init__.py
    Foo.py
    module1.py
    module2.py
    module3.py

包名是foo,在它下面我有模块Foo.py,其中包含类Foo的代码。因此,我使用相同的名称作为包,模块和类,这可能不是很聪明的开始。

__init__.py是空的,类Foo需要导入module1, module2 and module3因此我的Foo.py文件的一部分看起来像:

# foo/Foo.py

import module1
import module2
import module3

class Foo(object):
    def __init__(self):
....
....
if __name__ == '__main__':
    foo_obj = Foo()

但是后来我重新考虑了这一点,我认为在__init__.py文件中包含所有导入会更好。因此我的__init__.py现在看起来像:

# foo/__init__.py

import Foo
import module1
import module2
import module3
....
....

而我的Foo.py只需要导入foo

# foo/Foo.py

import foo

虽然这看起来很方便,因为它是一个班轮,我有点担心它可能会创建循环导入。我的意思是,当脚本Foo.py运行时,它将导入它可以的所有内容,然后将调用__init__.py,它将再次导入Foo.py(这是正确的吗?)。另外,对于包,模块和类使用相同的名称会使事情更加混乱。

这样做是否有意义?还是我在找麻烦?

python python-2.7 import
5个回答
26
投票

如果只是遵守一些流行的python约定和标准,你可以采取一些措施来改善你的组织。

如果您搜索此主题,您将不可避免地遇到推荐PEP8指南的人。这些是组织python代码的事实规范标准。

模块应具有简短的全小写名称。如果提高可读性,则可以在模块名称中使用下划线。 Python包也应该有简短的全小写名称,但不鼓励使用下划线。

根据这些指南,您的项目模块应如下命名:

foo/
    __init__.py
    foo.py
    module1.py
    module2.py
    module3.py

我发现通常最好避免在__init__.py中不必要地导入模块,除非你出于命名空间的原因这样做。例如,如果您希望包的命名空间如下所示

from foo import Foo

代替

from foo.foo import Foo

然后它是有道理的

from .foo import Foo

在你的__init__.py。随着您的包越来越大,一些用户可能不想使用所有的子包和模块,因此强制用户通过在__init__.py中隐式导入它们来等待所有这些模块加载是没有意义的。此外,您还必须考虑是否要将module1module2module3作为外部API的一部分。它们仅被Foo使用而不是最终用户吗?如果它们仅在内部使用,则不要将它们包含在__init__.py

我还建议使用absolute or explicit relative imports导入子模块。例如,在foo.py

Absolute

from foo import module1
from foo import module2
from foo import module3

Explicit Relative

from . import module1
from . import module2
from . import module3

这将防止与其他包和模块的任何可能的命名问题。如果您决定支持Python3,它也会更容易,因为Python3不支持您当前使用的隐式相对导入语法。

此外,包中的文件通常不应包含

if __name__ == '__main__'

这是因为将文件作为脚本运行意味着它不会被视为它所属的包的一部分,因此它将无法进行相对导入。

向用户提供可执行脚本的最佳方法是使用scripts or console_scripts feature of setuptools。您组织脚本的方式可能会有所不同,具体取决于您使用的方法,但我通常会按照以下方式组织我的:

foo/
    __init__.py
    foo.py
    ...
scripts/
     foo_script.py
setup.py

4
投票

PEP 0008, "Public and internal interfaces"说:

应始终将导入的名称视为实现细节。其他模块不能依赖于对这些导入名称的间接访问,除非它们是包含模块的API的显式记录部分,例如os.path或从子模块公开功能的包的__init__模块。

因此,如果使用__init__来公开子模块中的函数,这表明可以将导入放在__init__模块中。 Here是一篇简短的博客文章,我发现了一些使用__init__的Pythonic用法的例子,使用导入在包级别提供子包。

您将导入语句移动到__init__以便在Foo中只有一个导入的示例似乎不遵循此规则。我的解释是你的__init__中的导入应该用于外部接口,否则,只需将你的import语句放在需要它们的文件中。这可以帮助您在子模块名称更改时省去麻烦,并在添加更多使用不同子模块子集的文件时使您免于不必要或难以查找的导入。

至于循环引用,这在Python(for example)中肯定是可能的。在我实际尝试你的玩具示例之前,我写过这个,但为了使示例工作我必须将Foo.py移动到一个级别,如下所示:

Foo.py
foo/
    __init__.py
    module1.py
    module2.py
    module3.py

使用该设置和一些打印语句,运行python Foo.py给出输出:

module 1
module 2
module 3
hello Foo constructor

并正常退出。请注意,这是因为添加了if __name__ == "__main__" - 如果在其之外添加print语句,您可以看到Python仍在加载模块两次。更好的解决方案是从__init__.py中删除导入。正如我之前所说,根据这些子模块的不同,这可能有意义也可能没有意义。


1
投票

您可以参考“Python代码样式指南”获取最佳实践,导入将保留在该指南的类中。

https://www.python.org/dev/peps/pep-0008/#imports


1
投票

试试这个:

包1

package1.朋友

__init__.朋友

包装2

test.朋友

package1.朋友:-

class abc:
    a = 'hello'
    def print_a(self):
    print(a)

init.朋友:-

from .package1 import abc

package2.朋友:-

From package1.package1 import abc

我使用这些__init__.py从包中导入。


0
投票

如果这是正确的方法,我无法明确说明,但我总是以前一种方式做到这一点。也就是说,我一直保持__init__.py空,只是根据需要在Foo.py内输入东西。

从你描述它的方式来看,似乎在后一种形式中发生了一些循环逻辑。

© www.soinside.com 2019 - 2024. All rights reserved.