模块导入和__ init__. py

我试图了解 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(对吗?).此外,对包、模块和类使用相同的名称会使事情更加混乱。

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

78844 次浏览

我不能确定这是不是正确的方法,但我一直都是用前一种方法。也就是说,我一直保持 __init__.py为空,只是根据需要导入 Foo.py中的内容。

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

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

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

根据 PEP 0008,“公共和内部接口”:

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

因此,如果使用 __init__公开子模块中的函数,那么建议 将导入放在 __init__模块中。给你是我发现的一篇短博客文章,其中有几个 Python 使用 __init__的例子,使用导入使子包在包级别可用。

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

至于循环引用,这在 Python (比如说)中是完全可能的。在我尝试你的玩具示例之前,我已经写过这个了,但是为了让这个示例起作用,我必须把 Foo.py提高一个级别,比如:

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

通过这个设置和一些 print 语句,运行 python Foo.py会得到输出:

module 1
module 2
module 3
hello Foo constructor

然后正常离开。注意,这是由于添加了 if __name__ == "__main__"-如果您在其外添加了 print 语句,您可以看到 Python 仍然加载了两次模块。更好的解决方案是从 __init__.py中删除导入。正如我前面所说,这可能有意义,也可能没有意义,这取决于这些子模块是什么。

如果只是为了遵守一些流行的 Python 约定和标准,您可以做一些事情来改进您的组织。

如果您搜索这个主题,您将不可避免地遇到推荐 PEP8指南的人。这些是组织 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

我还建议使用 绝对或明确的相对进口导入子模块

当然

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

显性亲属

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

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

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

if __name__ == '__main__'

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

向用户提供可执行脚本的最佳方式是使用 ABC2的 ABC0或 console_scripts特征。您组织脚本的方式可能因您使用的方法不同而有所不同,但我通常是这样组织我的脚本的:

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

试试这个:

包裹1

Package1.py

不,不,不,不,不,不,不,不,不,不,不,不,不

包裹2

Test.py

Package 1.py:-

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

_ _ init _ _. py:-

from .package1 import abc

Package2.py:-

From package1.package1 import abc

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