# Python教程 - 9 模块和包
# 9.1 模块
什么是Python模块?
每一个以 .py
结尾的python文件就是一个模块。
模块能定义全局变量、函数、和类,也能包含可执行的代码。
模块有什么的作用?
模块就好比工具包,Python中有很多各种不同的模块,每个模块可以帮助我们快速实现一些功能,只需要引入使用即可,不用我们去编写实现,大大提高开发效率,例如之前使用的随机数模块。
# 9.1.1 模块的导入
模块导入有三种方式:
# 1 导入整个模块
语法:
import 模块名 # 导入单个模块
import 模块名1, 模块名2 # 导入多个模块
2
导入之后,就可以通过 模块名.
来调用模块中的全局变量、函数、类。
举个栗子:
import time # 导入时间模块
print("开始")
time.sleep(1) # 让程序睡眠1秒(阻塞)
print("结束")
2
3
4
5
如果模块的名称太长,我们还可以为模块起别名:
语法:
import 模块名1 as 别名 # 模块可以起别名
起别名的时候,建议遵循大驼峰命名法(各单词首字母大写)
举个栗子:
import time as tm # 导入时间模块
print("开始")
tm.sleep(1) # 让程序睡眠1秒(阻塞)
print("结束")
2
3
4
5
# 2 导入模块中的部分功能
模块中有很多功能,但是我们只想使用某一个全局变量、函数或类,那么可以单独导入这个功能。
语法:
from 模块名 import 功能名
from 模块名 import 功能名 as 别名 # 功能也可以起别名
2
举个栗子:
from time import sleep # 只导入模块中的sleep方法
print("开始")
sleep(1) # 让程序睡眠1秒
print("结束")
2
3
4
5
# 3 导入模块中所有的函数
该方式不推荐,函数出现重名没有任何提示,出现问题不好排查。
语法:
from 模块名 import *
举个栗子:
from time import * # 只导入模块中所有的函数
print("开始")
sleep(1) # 让程序睡眠1秒
print("结束")
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)
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)
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")
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()
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")
2
编写module_two.py:
def test_two():
print("test_two")
2
# 2 导入包
方式一:导入包中指定的模块:
import 包名.模块名 # 导入包
包名.模块名.功能 # 使用包中的功能
2
举个例子,调用上面定义的包(package_one)中的module_one模块中的函数:
import package_one.module_one # 导入包中的模块
package_one.module_one.test_one() # 调用模块中的功能
2
3
这种方式调用的时候太繁琐,我们可以起个别名:
import package_one.module_one as one # 导入包中的模块
one.test_one() # 调用模块中的功能
2
3
也可以使用 from 包名 import 模块名
语法:
from package_one import module_tow
module_tow.test_two()
2
3
当然,功能也可以起别名:
from package_one import module_tow as two
two.test_two()
2
3
方式二:导入包中所有的模块
使用该方式,必须先在 __init__.py
文件中添加 __all__ = []
,用来控制允许导入的模块列表,否则无法导入。
修改package_one包下的 __init__.py
文件,设置允许导入的模块名称。
然后在main.py模块中导入并调用包中的两个模块:
# 9.2.2 安装第三方包
在Python程序的生态中,有许多非常多的第三方包(非Python官方),可以极大的帮助我们提高开发效率,例如:
科学计算中常用的:numpy包
人工智能常用的:tensorflow
这些第三方的包,极大的丰富了Python的生态,提高了开发效率。但是由于是第三方,所以Python没有内置,所以需要安装它们才可以导入使用。
安装第三方包,需要在命令行中执行语句:
pip install 包名称
由于pip是连接的国外的网站进行包的下载,所以有的时候速度很慢。
我们可以通过如下命令,让其连接国内的网站进行包的安装:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 包名称
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
2
前面是第三方的包的名称,后面是对应的版本号。
我们可以将 requirements.txt
文件放在项目根目录下,然后在命令行中进入项目根目录,执行如下命令,就可以一键安装requirements.txt
文件中配置的依赖了:
pip install -r requirements.txt
requirements.txt
文件一般不是手动编写,使用工具生成即可。
常用的工具是:pip freeze
和 pipreqs
。
# 9.3.1 使用pip freeze生成
在项目根目录下执行如下命令,就会在根目录下生成 requirements.txt
文件:
pip freeze > requirements.txt
这个命令会列出当前环境中所有已安装的包及其版本号,很多依赖项目没有用到也会添加进来,所以不推荐这种方式。
# 9.3.2 使用pipreqs生成(推荐)
pipreqs
是一个更智能的工具,它根据项目文件中的实际导入的依赖来生成 requirements.txt
文件。
首先需要安装pipreqs
pip install pipreqs
然后在项目根目录下运行:
pipreqs ./ --encoding=utf8
# 强制生成,会覆盖已有的
pipreqs ./ --encoding=utf8 --force
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 虚拟环境就是为了解决这个问题,使用虚拟环境,可以为每个项目创建一个独立的运行环境,每个环境中只安装该项目所需的库,这样项目之间能够独立运行,不会产生影响。
常用的虚拟环境工具:
- venv(Python 3.3+ 内置)
- virtualenv(支持 Python 2 和 3)
- Conda(适用于 Python 和其他语言)
下面使用 venv 来创建虚拟环境。
# 9.4.1 创建虚拟环境
运行如下命令创建虚拟环境:
python -m venv 虚拟环境名称
# 举个栗子
python -m venv my-env
2
3
4
my-env
是虚拟环境的目录名,可以根据需要自定义。
运行完后,会在当前目录下创建一个虚拟环境名称的目录。
# 9.4.2 删除虚拟环境
因为创建了虚拟环境,会创建一个虚拟环境的目录,所以删除虚拟环境直接将虚拟环境的目录删掉就可以了。
rm -rf 虚拟环境名称
# 9.4.3 激活虚拟环境
my-env
是上面创建的虚拟环境的名称。
在 Windows 上:
my-env\Scripts\activate
在 Linux、Unix 或 MacOS 上:
source my-env/bin/activate
激活虚拟环境后,命令行提示符前面通常会显示虚拟环境的名称,表示当前环境已激活。
激活虚拟环境以后,执行的命令就是在虚拟环境中的,例如执行安装依赖或者 python xxx.py 等命令,项目拥有的就是独立的空间。
# 9.4.4 安装依赖
激活虚拟环境后,就可以在当前虚拟环境中使用 pip
安装项目所需的依赖:
pip install <package_name>
# 通过 requirements.txt 安装所有依赖
pip install -r requirements.txt
2
3
4
并在虚拟环境中运行我们的python程序:
# 运行程序
python main.py
2
# 9.4.5 退出虚拟环境
使用以下命令可以退出虚拟环境,返回全局环境:
deactivate
总结:
使用虚拟环境可以解决项目的依赖冲突问题,简化环境配置,提高开发和部署的效率。
激活虚拟环境以后,执行的命令就是在虚拟环境中的,例如执行安装依赖或者 python xxx.py 等命令,项目拥有的就是独立的空间。