python中的模块、库、包有什么区别?

Author: [风影忍着]

Link: [https://www.zhihu.com/question/30082392/answer/2030353759]

一、模块(module)

模块,英文为Modules,本质上是一个Python程序,以.py作为文件后缀。任何py文件都可以作为一个模块。

*其他可作为module的文件类型还有“.so”、”.pyo”、”.pyc”、”.dll”、”.pyd”,但Python初学者几乎用不到。

通过使用模块,可以有效地避免命名空间的冲突,可以隐藏代码细节让我们专注于高层的逻辑,还可以将一个较大的程序分为多个文件,提升代码的可维护性和可重用性。

(一)导入

导入一个模块一般有两种方法:

  1. import 模块名1 [as 别名1], 模块名2 [as 别名2],…。此时,模块名1本身被导入,但保存它原有的命名空间,所以我们需要用”模块名1.成员名1“方式访问其函数或变量。
  2. from 模块名 import 成员名1 [as 别名1],成员名2 [as 别名2],…。此时,会将该模块的函数/变量导入到当前模块的命名空间中,无须用“模块名1.成员名1”访问了。

其中,用 [] 括起来的部分,可以使用,也可以省略。

注意,模块名里面只要写py文件的名字,不需要加后缀。

另外,尽量不要使用

1
from demo import *

的方式进行导入,否则很容易出现名称重复的情况。

例如,如果有两个模块里面有相同的成员,或者你自己定义了某一个对象和demo module里面的成员(例如函数)重名了,运行的时候会出问题。因为此时我们无法确认,我们所指的是自己定义的对象还是demo module里面的成员。

(二)if __name__ == ‘__main__‘:

在完成一个模块的编写之前,我们一般会对模块中的功能进行测试,看看各项功能是否正常运行。对于这些测试的代码,我们希望只在直接运行这个py文件的时候执行,在用其他的程序import这个模块的时候不要执行。这个时候就要借助Python内置的__name__变量。

__name__变量的取值是什么可以分两种情况,

  1. 当一个py文件被直接运行的时候,__name__变量的值为__main__。
  2. 当一个py文件被import到其他程序的时候,这个py文件里面的__name__变量的值为这个py文件的名字。

我们可以利用__name__变量的这个特点,结合一个if语句,让我们import某一个模块的时候只用到它提供的功能,而不要运行里面用来测试的代码:

1
2
if __name__ == '__main__':
# put your code here

(三)模块的说明文档

模块的说明文档放在py文件的开头,用成对的三个英文单引号引起来:

1
2
3
'''
模块的说明文档放在这里。
'''

然后我们可以用模块的__doc__属性来访问模块的说明文档:

1
2
import demo
print(demo.__doc__)

在调用这个模块来写代码的时候,也可以阅读到模块的说明文档。例如下图,我把鼠标放在导入的 re 包名称上面就能看到其说明文档:


在编程中显示的说明文档的内容和原始 py 文件中的内容一样。例如,下面是 re 模块的原始 py 文件中的说明文档:


(四)模块的路径

对于用import语句导入的模块,Python会按照下面的路径列表顺序地查找我们需要的模块:

  1. 当前的工作目录;
  2. PYTHONPATH(环境变量)中的每一个目录;
  3. Python 默认的安装目录。

如果我们在导入自己写的模块的时候,Python解释器提示找不到这个模块

1
ModuleNotFoundError: No module named '模块名'

那么,说明我们写的模块没有放在上述三类路径。由于这三类目录都保存在标准模块sys的sys.path变量中,因此我们有三种解决方法。

1.向sys.path变量中临时添加模块文件所在的完整路径。

1
2
import sys
sys.path.append(r'D:\xxx\yyy')

2.将模块移动到sys.path 变量中已包含的路径中。

3.修改path 系统的环境变量。

其中,环境变量又分为用户变量(仅对当前用户生效)和系统变量(对系统里所有用户均生效)。Python在使用path变量时,会先按照系统path变量找,然后按照用户path变量的路径找。

通常情况下,设置用户path变量即可。

二、包(package)

在比较大型的项目中常常需要编写、用到大量的模块,此时我们可以使用包(Package)来管理这些模块。

(一)什么是包?

Python包,就是里面装了一个__init__.py文件的文件夹。

__init__.py文件(前后各有 2 个下划线’_‘)具有下面4个性质

  1. 它本身是一个模块;
  2. 这个模块的模块名不是__init__,而是这个包的名字,也就是装着__init__.py文件的文件夹的名字。
  3. 它的作用是将一个文件夹变为一个Python模块
  4. 它可以不包含代码,不过此时仅仅用import [该包]形式是什么也做不了的。所以一般会包含一些Python初始化代码,在这个包被import的时候,这些代码会自动被执行。
  5. 第4点所指的初始化代码类型一:批量导入我们需要用到的模块,这样我们就不用在用到的时候再一一导入,方便实用。
  6. 第4点所指的初始化代码类型二:如果我们要使用“from pacakge_1 import *”的形式导入一个模块里面的所有内容,则需在__init__.py中加上“all = [‘file_a’, ‘file_b’]”。其中,package_1下有file_a.py和file_b.py。
  7. 不建议在__init__.py中写类,以保证该py文件简单。

__all__是Python中的一个重要的变量,放在__init__模块中,用于指定此包(package)被import *时,哪些模块(module)会被import进当前作用域中。不在 __all__列表中的模块不会被其他程序引用。我们可以对 __all__进行重写。

__path__也是python中的一个常用变量,它是储存着当前包内的搜索路径的一个列表。默认情况下只有一个元素,即当前包(package)的路径。

Python包具有下面3个性质

  1. 它实质上是一个文件夹;
  2. 该文件夹里面一定有__init__.py模块,其他的模块可以有也可以没有;
  3. 它的本质依然是模块,因此一个包里面还可以装其他的包。

(二)导入包

导入包的方法和导入模块比较类似,只不过由于层级比一般模块多了一级,所以多了一条导入形式

  1. import 包名[.模块名 [as 别名]]
  2. from 包名 import 模块名 [as 别名]
  3. from 包名.模块名 import 成员名 [as 别名]

我们在导入包的时候,实际上是导入了它的__init__.py文件文件。

最后总结一下,Python里面存在这样的关系

  • 数据可以封装在容器(列表、元组、字符串、字典)里面;

  • 代码可以封装在function里面;

  • function和数据可以封装在class里面(或者说方法和属性可以封装在类里面);

  • 上述三类内容可以打包在module(模块)里面;

  • 多个module可以打包在package(包)里面;

  • 多个package可以打包在library(库)里面。

此外,关于“库(library)”的概念

https://www.zhihu.com/people/29a618a6a8a39a1683934e8436a1aaf4 在评论区对于“库(library)”的概念做了很好的补充,使得这篇科普更加的完整。请观众老爷移步评论区,阅读本文唯一的一条精选评论。

https://www.zhihu.com/people/5f4b043a1b4b317a293982593a443b1d https://www.zhihu.com/question/30082392/answer/2315826832,在文末的部分,将库 (library) 和框架 (framework) 对比起来讲解,是另外一个理解库的角度。