【Python】模块(Module)、包(Package)以及相对导入(relative import)和绝对导入(absolute import)

Python程序架构

实际上,程序通常涉及不只一个文件。除了最简单的脚本之外,程序一般将采用多文件系统的形式,即使能够自己编写单个文件,也一定会使用到其他人已经写好的外部文件。Python程序架构是将一个程序分割为源代码文件(也就是模块)的集合,并将这些集合连接成整体的方式。正如我们将要看到的那样,Python鼓励模块化的程序结构,将功能相近的可重用单元组织在一个模块中,这种方式符合直觉,同时也合乎直觉。

从本质上讲,一个Python程序包括了多个含有Python语句的文本文件。程序拥有一个主体的顶层文件,辅以零个或多个被称为模块的支持文件。
顶层文件(又称为脚本,也可称为顶层模块)包含了程序的主要控制流程:这就是你用来启动应用程序的文件。而模块文件是工具库,这些文件中收集了顶层文件(或者其他可能的地方)要使用的组件。顶层文件使用了在模块文件中定义的工具,而这些模块又有可能使用了其他模块所定义的工具。尽管模块文件也是代码文件,但它们通常在运行时不需直接做任何事。作为替代,它们定义的工具会在其他文件中使用。在Python中,一个文件通过导人一个模块来获得这个模块定义的工具的访问权,这些工具被认为是这个模块的属性(即附加到模块对象的名称,例如函数)。

下图是一个包含三个文件的Python程序的草图:​​a.py​​​、​​b.py​​​和​​c.py​​​。文件​​a.py​​​是顶层文件,它是一个由语句组成的简单文本文件,在运行时这些语句将从上至下执行。文件​​b.py​​​和​​c.py​​​是模块,它们也是含有语句的简单文本文件,但是它们通常并不是直接运行。
在这里插入图片描述

模块(Module)

概念: 在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护。为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式。
好处: 使用模块可以避免函数名和变量名冲突,相同名字的函数和变量完全可以分别存在不同的模块中,因此,我们自己在编写模块时,不必考虑名字会与其他模块冲突。但是也要注意,尽量不要与内置函数名字冲突。当一个模块编写完毕,就可以被其他地方引用,因此在编程时代码不必从零开始写。

在Python中,一个.py文件就称之为一个模块(Module),python中模块一共有以下三种:

  • python标准库(又称内置模块)
  • 开源模块(又称第三方模块)
  • 自定义模块

模块的导入

导入模块时,解释器按照 sys.path 给出的路径顺序查找要导入的模块,在sys.path中,当前路径具有最高优先级,所以模块命名不可与 Python 自带模块或第三方模块命名冲突,也不可和类,全局变量,内建函数命名冲突。sys.path 是一个路径的列表变量, 可以添加指定的路径:sys.path.append('XXX')

有以下几种方式导入模块、导入模块中的变量和函数:

import 模块名
import 模块名 as 别名
from 模块名 import 变量(函数)
from 模块名 import 变量(函数)as 别名
from module0 import *   		# 不建议,可能会导致名字空间冲突

包(Package)

相同名字的变量和函数名可以存放在不同模块下来避免冲突,但如果不同的模块名相同怎么办?为了避免模块名冲突,Python又引入了按目录来组织模块的方法,称为包(Package)。

请注意,每一个包目录下面都会有一个__init__.py的文件,这个文件是必须存在的,否则,Python就把这个目录当成普通目录(文件夹),而不是一个包。init.py可以是空文件,也可以有Python代码,因为__init__.py本身就是一个模块,而它的模块名就是对应包的名字。调用包就是执行包下的__init__.py文件

包外导入

├── package0
│   ├── __init__.py
│   └── module0.py
├── package1
│   ├── __init__.py
│   └── module0.py
└── test_package.py

以上是示例的目录结构,由于包是一个目录,那么导入包中的模块时,就要指明模块所在包中的路径。在test_package.py中可以使用以下几种方式进行导入:
1)使用import 导入: import 参数的最后部分必须是模块名(文件名),而不能是包名(目录名),比如 import package0

import 包名.模块名
# import package0.module0		
import 包名.模块名 as 别名
# import package1.module0 as p1_m0

2)使用from导入:from 后的参数可以为包路径,也可以为模块名路径,并且可以精确指定 import 模块中各成员

from 包名 import *			# 可以导入 package0 包中所有模块		
# from package0 import *  
from 包名 import 模块名 as 别名
# from package0 import module0 as mo
from 包名.模块名 import 函数(变量)as 别名
from package0.module0 import module_info as mo

尽管可以通过 from package0 import * 导入包中的所有模块或子包,但这种方法不要使用,很容易造成命名冲突,代码不清晰,导入应该明确要导入的模块名。

包内导入:相对导入与绝对导入

Python 相对导入与绝对导入,这两个概念是相对于包内导入而言的。包内导入即是包内的模块导入包内部的模块。所谓的包,就是包含 init.py 文件的目录,该文件在包导入时会被首先执行,该文件可以为空,也可以在其中加入任意合法的 Python 代码。

如果是绝对导入,一个模块只能导入自身的子模块或和它的顶层模块同级别的模块及其子模块
如果是相对导入,一个模块必须有包结构且只能导入它的顶层模块内部的模块

绝对导入格式:

import A.B
from A import B

相对导入格式:

from . import B
from ..A import B

代表当前模块,…代表上层模块,…代表上上层模块,依次类推。相对导入可以避免硬编码带来的维护问题,例如我们改了某一顶层包的名,那么其子包所有的导入就都不能用了。但存在相对导入语句的模块,不能直接运行,否则会有异常ValueError: Attempted relative import in non-package

在程序的顶层文件(执行文件)中,一般使用绝对导入,在其他辅助性模块文件中,可以使用相对导入。

参考文章:
[1] 模块与包的概念
[2] 系统学习Python——模块和包:Python程序架构
[3] python之基础篇(八)——模块与包

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值