返回介绍

建议18:构建合理的包层次来管理 module

发布于 2024-01-30 22:19:09 字数 3036 浏览 0 评论 0 收藏 0

我们知道,本质上每一个Python文件都是一个模块,使用模块可以增强代码的可维护性和可重用性。但显然在大的项目中将所有的Python文件放在一个目录下并不是一个值得推荐的做法,我们需要合理地组织项目的层次来管理模块,这就是包(Package)发挥功效的地方了。

什么是包呢?简单说包即是目录,但与普通目录不同,它除了包含常规的Python文件(也就是模块)以外,还包含一个__init__.py文件,同时它允许嵌套。包结构如下:

Package/ __init__.py
   Module1.py
   Module2.py
   Subpackage/ __init__.py
     Module1.py
     Module2.py

包中的模块可以通过“.”访问符进行访问,即“包名.模块名”。如上述嵌套结构中访问Package目录下的Module1可以使用Package.Module1,而访问Subpackage中的Module1则可以使用Package.Subpackage.Module1。包中的模块同样可以被导入其他模块中。有以下几种导入方法:

1)直接导入一个包,具体如下:

import Package

2)导入子模块或子包,包嵌套的情况下可以进行嵌套导入,具体如下:

from Package import Module1
import Package.Module1
from Package import Subpackage
import Package.Subpackage
From Package.Subpackage import Module1
import Package.Subpackage.Module1

前面提到在包对应的目录下包含有__init__.py文件,那么这个文件的作用是什么呢?它最明显的作用就是使包和普通目录区分;其次可以在该文件中申明模块级别的import语句从而使其变成包级别可见。上例所示的结构中,如果要import包Package下Module1中的类Test,当__init__.py文件为空的时候需要使用完整的路径来申明import语句:

from Package.Module1 import Test

但如果在__init__.py文件中添加from Module1 import Test语句,则可以直接使用from Package import Test来导入类Test。需要注意的是,如果__init__.py文件为空,当意图使用from Package import *将包Package中所有的模块导入当前名字空间时并不能使得导入的模块生效,这是因为不同平台间的文件的命名规则不同,Python解释器并不能正确判定模块在对应的平台该如何导入,因此它仅仅执行__init__.py文件,如果要控制模块的导入,则需要对__init__.py文件做修改。

__init__.py文件还有一个作用就是通过在该文件中定义__all__变量,控制需要导入的子包或者模块。在上例的Package目录下的__init__.py文件中添加:

__all__ = ['Module1', 'Module2','Subpackage']

之后再运行from Package import *,可以看到__all__ 变量中定义的模块和包被导入当前名字空间。

>>> from Package import *
>>> dir()
['Module1', 'Module2', 'Subpackage', '__builtins__', '__doc__', '__name__', '__package__']

包的使用能够带来以下便利:

合理组织代码,便于维护和使用。通过将关系密切的模块组织成一个包,使项目结构更为完善和合理,从而增强代码的可维护性和实用性。以下是一个可供参考的Python项目结构:

ProjectName/
|---README
  |----LICENSE
  |----setup.py
  |-----requirements.txt
  |------sample/
  |  |----__init__.py
  |  |----core.py
  |  |----helpers.py
  |------docs/
  |  |------conf.py
  |  |------index.rst
  |------bin/
  |------package/
  |  |-----__init__.py
  |  |-----subpackage/
  |  |------........
  |------tests/
  |  |------test_basic.py
  |  |------test_advanced.py

能够有效地避免名称空间冲突。使用from Package import Module2 可以将Module2导入当前局部名字空间,访问的时候不再需要加入包名。看下面这个例子:

>>> from Package import Module2
>>> Module2.Hi()
Hi from Package Module1

上述代码中Subpackage中也包含Module2,当使用from... import...导入的时候,生效的是Subpackage的Module2。结果如下:

>>> from Package.Subpackage import Module2
>>> Module2.Hi()
Hi from Subpackage Module2

如果模块包含的属性和方法存在同名冲突,使用import module可以有效地避免名称冲突。在嵌套的包结构中,每一个模块都以其所在的完整路径作为其前缀,因此,即使名称一样,但由于模块所对应的其前缀不同,因此不会产生冲突。

>>> import Package.Module2
>>> Package.Module2.Hi()
Hi from Package Module1
>>> import Package.Subpackage.Module2
>>> Package.Subpackage.Module2.Hi()
Hi from Subpackage Module2

注意:本节所说的包与后文中谈到的软件包不同,这里的包的概念仅限于包含一个或一系列Python文件(模块)的文件夹(目录),它的作用是合理组织代码,便于维护和使用,并避免命名冲突。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文