我试图了解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
(这是正确的吗?)。另外,对于包,模块和类使用相同的名称会使事情更加混乱。
我的方式是否有意义?还是我在找麻烦?
答案 0 :(得分:24)
如果只是为了遵守一些流行的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
中。随着您的软件包变大,一些用户可能不想使用所有子软件包和模块,因此强制用户等待所有这些模块加载是没有意义的,方法是在{ {1}}。此外,您还需要考虑是否要将__init__.py
,module1
和module2
作为外部API的一部分。它们仅由module3
使用而不是最终用户吗?如果它们仅在内部使用,则请勿将其包含在Foo
我还建议使用absolute or explicit relative imports导入子模块。例如,在__init__.py
foo.py
from foo import module1
from foo import module2
from foo import module3
这将防止其他软件包和模块出现任何可能的命名问题。如果您决定支持Python3,它也会更容易,因为Python3不支持您当前使用的隐式相对导入语法。
此外,您的包中的文件通常不应包含
from . import module1
from . import module2
from . import module3
这是因为将文件作为脚本运行意味着它不会被视为它所属的包的一部分,因此它无法进行相对导入。
向用户提供可执行脚本的最佳方法是使用scripts
or console_scripts
feature of setuptools
。组织脚本的方式可能会有所不同,具体取决于您使用的方法,但我通常会按照以下方式组织我的:
if __name__ == '__main__'
答案 1 :(得分:4)
根据PEP 0008, "Public and internal interfaces":
导入的名称应始终被视为实施细节。其他模块不得依赖于对这些导入名称的间接访问,除非它们是包含模块的API的明确记录部分,例如os.path或包含
__init__
模块的公开功能来自子模块。
因此,如果__init__
被用于公开子模块中的函数,那么这表明 可以将导入放在__init__
模块中。 Here是一篇简短的博客文章,我发现了几个Pythonic使用__init__
的例子,使用导入在包级别提供子包。
您将导入语句移至__init__
以便在Foo
中只有一个导入的示例不似乎遵循此规则。我的解释是,__init__
中的导入应该用于外部接口,否则,只需将导入语句放在需要它们的文件中。这可以帮助您在子模块名称更改时省去麻烦,并在添加更多使用不同子模块子集的文件时使您免于不必要或难以查找的导入。
就循环引用而言,这在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
中删除导入。正如我之前所说,根据这些子模块的不同,这可能有意义也可能没有意义。
答案 2 :(得分:1)
您可以参考“Python代码样式指南”'对于最佳实践,导入将保留在该指南的课程中。
答案 3 :(得分:0)
如果这是正确的方法,我无法明确说明,但我总是以前一种方式做到这一点。也就是说,我一直保持 init .py为空,只需根据需要在Foo.py中导入内容。
从你描述它的方式来看,似乎有一些循环逻辑以后一种形式发生。
答案 4 :(得分:0)
尝试一下:
package1.py
__ init __。py
test.py
class abc:
a = 'hello'
def print_a(self):
print(a)
from .package1 import abc
From package1.package1 import abc
我使用这些__init__.py从包中导入。