# Python教程 - 9 模块和包

# 9.1 模块

什么是Python模块?

每一个以 .py 结尾的python文件就是一个模块。

模块能定义全局变量、函数、和类,也能包含可执行的代码。

模块有什么的作用?

模块就好比工具包,Python中有很多各种不同的模块,每个模块可以帮助我们快速实现一些功能,只需要引入使用即可,不用我们去编写实现,大大提高开发效率,例如之前使用的随机数模块。

# 9.1.1 模块的导入

模块导入有三种方式:

# 1 导入整个模块

语法:

import 模块名								# 导入单个模块
import 模块名1, 模块名2				# 导入多个模块
1
2

导入之后,就可以通过 模块名. 来调用模块中的全局变量、函数、类。

举个栗子:

import time             # 导入时间模块

print("开始")
time.sleep(1)          # 让程序睡眠1秒(阻塞)
print("结束")
1
2
3
4
5

如果模块的名称太长,我们还可以为模块起别名:

语法:

import 模块名1 as 别名					# 模块可以起别名
1

起别名的时候,建议遵循大驼峰命名法(各单词首字母大写)

举个栗子:

import time as tm            # 导入时间模块

print("开始")
tm.sleep(1)          # 让程序睡眠1秒(阻塞)
print("结束")
1
2
3
4
5

# 2 导入模块中的部分功能

模块中有很多功能,但是我们只想使用某一个全局变量、函数或类,那么可以单独导入这个功能。

语法:

from 模块名 import 功能名
from 模块名 import 功能名 as 别名			# 功能也可以起别名
1
2

举个栗子:

from time import sleep      # 只导入模块中的sleep方法

print("开始")
sleep(1)               # 让程序睡眠1秒
print("结束")
1
2
3
4
5

# 3 导入模块中所有的函数

该方式不推荐,函数出现重名没有任何提示,出现问题不好排查。

语法:

from 模块名 import *
1

举个栗子:

from time import *      # 只导入模块中所有的函数

print("开始")
sleep(1)                # 让程序睡眠1秒
print("结束")
1
2
3
4
5

# 4 导入的注意点

如果两个模块,存在同名的函数,那么先导入模块的函数,会被后导入的函数覆盖;

开发时 import 代码应该统一写在代码的顶部,更容易及时发现冲突;

一旦发现冲突,可以使用 as 关键字给其中一个元素起别名。

# 9.1.2 模块的搜索顺序

python解释器在导入模块的时候,会搜索当前目录指定模块名的文件,如果有就直接导入,如果没有在搜索系统目录。

所以在开发的时候,给文件起名,不要和系统的模块文件重名,否则会导致调用的系统功能无效。

举个栗子:

import random										# 导入 random 包
num = random.randint(1, 10)			# 返回一个 1 到 10 之间的数字,包括1和10
print(num)
1
2
3

如果当前目录下,存在一个 random.py 的文件,程序就无法正常执行了!因为会加载当前目录下的 random.py 文件,不加载系统的 random 模块。

# 9.1.3 自定义模块

我们在实际开发的时候,不可能所有的功能都写在一个文件中,所以就需要定义很多的模块。

例如我们新建一个模块,module_one.py

在实际的开发中,我们编写完一个模块,肯定是需要测试了,为了方便,一般会在模块中添加代码进行测试。

就像上面的代码,我们先编写了一个函数,然后在下面编写了代码调用了函数,来测试函数是否能正常运行。

然后,我们在main.py中引入module_one模块,然后调用其中的test_fun()函数:

运行main.py,执行结果:

会发现被引入的module_one模块中的test_fun()函数被执行了2次,这是为什么呢?

这是因为在导入模块的时候,模块中没有任何缩进的代码语句都会被执行一遍,所以上面在导入module_one模块的时候,模块中的 test_fun(1, 2) 语句会被执行一次,后面调用又执行了一次。

那么怎么解决这个问题呢?

需要用到 __name__ 属性。__name__Python 的一个内置属性,记录着一个字符串:

如果是当前执行的程序, __name____main__

如果是被其他文件导入的,__name__ 就是 当前的模块名

那么我们可以在module_one.py中这样定义:

def test_fun(a, b):
    print(a + b)


if __name__ == "__main__":			# 这样在执行当前模块的时候,条件才成立
    test_fun(1, 2)
1
2
3
4
5
6

# 9.1.4 导入限制

如果一个模块文件中定义了__all__ 变量,当使用 from xxx import * 导入时,只能导入这个列表中的元素。

例如我们定义了一个模块module_two.py

__all__ = ["test_a"]

def test_a():
    print("test_A")

def test_b():
    print("test_B")
1
2
3
4
5
6
7

在模块中定义了两个函数,但是在__all__ 变量中,只允许导出 test_a 函数。

那么在使用 from xxx import * 导入module_two的时候,只能调用 test_a 函数。

但是可以其他方式依旧是可以导入test_b()函数的,例如:

from module_two import test_b
test_b()
1
2

# 9.2 包

什么是包?

包就是一个文件夹,只是这个文件夹中包含了一个 __init__.py 文件,该文件夹可用于包含多个模块文件。

为什么需要包?

因为python中的模块太多了,通过包可以管理模块,包的作用就是包含多个模块,所以包的本质还是模块。

# 9.2.1 自定义包

# 1 创建包

在PyCharm中,右键项目或目录就可以新建包:

New -> Python Package -> 输入报名 -> OK

输入包名,就会创建一个包,自动创建 __init__.py 文件:

然后在包下新建两个模块,module_one.py 和 module_two.py:

并编写module_one.py:

def test_one():
    print("test_one")
1
2

编写module_two.py:

def test_two():
    print("test_two")
1
2

# 2 导入包

方式一:导入包中指定的模块:

import 包名.模块名			# 导入包
包名.模块名.功能				# 使用包中的功能
1
2

举个例子,调用上面定义的包(package_one)中的module_one模块中的函数:

import package_one.module_one					# 导入包中的模块

package_one.module_one.test_one()			# 调用模块中的功能
1
2
3

这种方式调用的时候太繁琐,我们可以起个别名:

import package_one.module_one as one					# 导入包中的模块

one.test_one()			                          # 调用模块中的功能
1
2
3

也可以使用 from 包名 import 模块名 语法:

from package_one import module_tow

module_tow.test_two()
1
2
3

当然,功能也可以起别名:

from package_one import module_tow as two

two.test_two()
1
2
3

方式二:导入包中所有的模块

使用该方式,必须先在 __init__.py文件中添加 __all__ = [],用来控制允许导入的模块列表,否则无法导入。

修改package_one包下的 __init__.py文件,设置允许导入的模块名称。

然后在main.py模块中导入并调用包中的两个模块:

# 9.2.2 安装第三方包

在Python程序的生态中,有许多非常多的第三方包(非Python官方),可以极大的帮助我们提高开发效率,例如:

  • 科学计算中常用的:numpy包

  • 人工智能常用的:tensorflow

这些第三方的包,极大的丰富了Python的生态,提高了开发效率。但是由于是第三方,所以Python没有内置,所以需要安装它们才可以导入使用。

安装第三方包,需要在命令行中执行语句:

pip install 包名称
1

由于pip是连接的国外的网站进行包的下载,所以有的时候速度很慢。

我们可以通过如下命令,让其连接国内的网站进行包的安装:

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 包名称
1

https://pypi.tuna.tsinghua.edu.cn/simple 是清华大学提供的一个网站,可供pip程序下载第三方包。

安装完第三方的包,我们就可以在代码中导入第三方的包进行使用了。

# 9.3 requirements.txt

requirements.txt 是什么?

在 Python 项目中,requirements.txt 记录了项目所需的所有依赖包及其版本号。我们在开发项目的时候,会安装很多第三方的依赖,如果将项目代码拷贝到另外一台机器运行,另外一台主机没有安装依赖肯定是跑不起来的,因为主机没有安装项目所需的依赖,requirements.txt 可以帮我们管理项目依赖,并一键下载依赖。

requirements.txt 文件格式如下:

numpy==1.21.0  
pandas>=1.3.0 
1
2

前面是第三方的包的名称,后面是对应的版本号。

我们可以将 requirements.txt 文件放在项目根目录下,然后在命令行中进入项目根目录,执行如下命令,就可以一键安装requirements.txt 文件中配置的依赖了:

pip install -r requirements.txt
1

requirements.txt 文件一般不是手动编写,使用工具生成即可。

常用的工具是:pip freezepipreqs

# 9.3.1 使用pip freeze生成

在项目根目录下执行如下命令,就会在根目录下生成 requirements.txt 文件:

pip freeze > requirements.txt
1

这个命令会列出当前环境中所有已安装的包及其版本号,很多依赖项目没有用到也会添加进来,所以不推荐这种方式。

# 9.3.2 使用pipreqs生成(推荐)

pipreqs 是一个更智能的工具,它根据项目文件中的实际导入的依赖来生成 requirements.txt 文件。

首先需要安装pipreqs

pip install pipreqs
1

然后在项目根目录下运行:

pipreqs ./ --encoding=utf8

# 强制生成,会覆盖已有的
pipreqs ./ --encoding=utf8 --force
1
2
3
4
  • --encoding=utf8 :使用utf8编码
  • --force :强制执行,会覆盖目录下已存在的requirements.txt文件;
  • ./:生成目录,./ 表示在当前目录生成。

pipreqs 会在项目文件夹中创建一个 requirements.txt 文件,只包含项目实际使用的依赖。


如果拿到的项目代码下有 requirements.txt 文件,直接使用 pip install -r requirements.txt 安装依赖即可。

如果没有 requirements.txt 文件,可以使用pipreqs生成,然后再一键安装。

# 9.4 虚拟环境

因为我们在一台机器上可能同时运行多个项目,项目通常会使用一些第三方的库,但是这些项目可能会依赖同一个第三方的库的不同版本,这样就产生依赖冲突。

Python 虚拟环境就是为了解决这个问题,使用虚拟环境,可以为每个项目创建一个独立的运行环境,每个环境中只安装该项目所需的库,这样项目之间能够独立运行,不会产生影响。


常用的虚拟环境工具:

  1. venv(Python 3.3+ 内置)
  2. virtualenv(支持 Python 2 和 3)
  3. Conda(适用于 Python 和其他语言)

下面使用 venv 来创建虚拟环境。

# 9.4.1 创建虚拟环境

运行如下命令创建虚拟环境:

python -m venv 虚拟环境名称

# 举个栗子
python -m venv my-env
1
2
3
4

my-env 是虚拟环境的目录名,可以根据需要自定义。

运行完后,会在当前目录下创建一个虚拟环境名称的目录。

# 9.4.2 删除虚拟环境

因为创建了虚拟环境,会创建一个虚拟环境的目录,所以删除虚拟环境直接将虚拟环境的目录删掉就可以了。

rm -rf 虚拟环境名称
1

# 9.4.3 激活虚拟环境

my-env 是上面创建的虚拟环境的名称。

在 Windows 上:

my-env\Scripts\activate
1

在 Linux、Unix 或 MacOS 上:

source my-env/bin/activate
1

激活虚拟环境后,命令行提示符前面通常会显示虚拟环境的名称,表示当前环境已激活。

激活虚拟环境以后,执行的命令就是在虚拟环境中的,例如执行安装依赖或者 python xxx.py 等命令,项目拥有的就是独立的空间。

# 9.4.4 安装依赖

激活虚拟环境后,就可以在当前虚拟环境中使用 pip 安装项目所需的依赖:

pip install <package_name>

# 通过 requirements.txt 安装所有依赖
pip install -r requirements.txt
1
2
3
4

并在虚拟环境中运行我们的python程序:

# 运行程序
python main.py
1
2

# 9.4.5 退出虚拟环境

使用以下命令可以退出虚拟环境,返回全局环境:

deactivate
1

总结:

使用虚拟环境可以解决项目的依赖冲突问题,简化环境配置,提高开发和部署的效率。

激活虚拟环境以后,执行的命令就是在虚拟环境中的,例如执行安装依赖或者 python xxx.py 等命令,项目拥有的就是独立的空间。