拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

【Python3基础】14-模块、包与虚拟环境

【Python3基础】14-模块、包与虚拟环境

▒ 目录 ▒

    • 🛫 导读
      • 本篇知识路线
    • 1️⃣ 自定义模块
      • 如何自己定义模块
      • 包
    • 2️⃣ 模块路径搜索
      • 模块路径搜索规则
      • 模块路径搜索注意事项
    • 3️⃣ 如何安装第三方模块
      • Python 直接安装
      • 使用 easy\_install 安装
      • 使用 pip 安装
    • 4️⃣ 虚拟环境
      • 安装和使用 virtualenv
      • 项目开发中使用虚拟环境
      • 注意事项
    • 5️⃣ 推荐的项目环境操作
    • 🛬 文章小结

🛫 导读

当代码不再只有一个文件,模块边界、导入路径和依赖隔离就会直接影响项目能否稳定运行。

本篇从自定义模块和包讲起,说明 Python 如何搜索模块,再介绍第三方模块安装与虚拟环境。原文保留了部分早期工具的使用背景;新项目应优先使用 Python 自带的venv创建环境,并通过python -m pip调用当前解释器对应的包管理工具。

本篇知识路线

项目目录 ├─ .venv/ 独立解释器与依赖 ├─ app/ 自己的包 │ ├─ __init__.py │ └─ main.py └─ requirements.txt import ──► 当前目录 / 环境 / 模块搜索路径

1️⃣ 自定义模块

在项目开发过程中,我们经常把项目中常用的功能或算法写成一个可供其它文件调用的函数或类,我们把这些函数和类放在一个文件中,这个文件就叫做模块,因为这个模块是我们自己写的,所以叫自定义模块。

模块很好的解决了软件工程中的低耦合,高内聚的要求,我们在项目开发中,把经常被使用的函数和类封装成模块是个很好的编程习惯。

如何自己定义模块

我们自己写个函数放在一个后缀名为 py 的文件里,如果我们在其它文件中使用该文件,这个文件相对使用它的文件就叫自定义模块。 自定义模块的引用方式是用 import… ,或者 from…import… ,但要注意自定义的模块和引用文件要放在一个目录下。下面我们实现一个定义模块,模块文件名为 mabs.py,我们通常叫模块名为 myabs(不带后缀名),在该模块内,我们实现一个函数 myabsfunc,该函数返回数字的绝对值,我们在 hello.py 文件中引用该自定义模块。

''' myabs.py 文件(该文件为自定义模块 myabs) 。 该模块有一个函数 myabsfunc。 '''defmyabsfunc(data):try:data=int(data)except:raiseTypeError("必须为数字类型")returndataifdata>=0else-data
''' hello.py 文件。 该文件引用自定义模块 myabs。 '''importmyabs# 导入自定义模块print(myabs.myabsfunc(-250))

当然我们也可以直接导入这个模块内的函数。

''' hello.py 文件。 该文件引用自定义模块 myabs。 '''frommyabsimportmyabsfunc# 从自定义模块 myabs 里导入函数print(myabsfunc(-250))

我们可以给 import… 后面的名字起个别名。

''' hello.py 文件。 该文件引用自定义模块 myabs。 '''frommyabsimportmyabsfuncasabsfunc# 给函数起个别名print(absfunc(-250))################################ 我是分割线 ################################''' hello.py 文件。 该文件引用自定义模块 myabs。 '''importmyabsasabsmodule# 给函数起个别名print(absmodule.myabsfunc(-250))

包

在开发项目中,我们可能要写很多模块,为了方便与组织,我们可以把这些模块放入一个目录中,Python 对按目录来组织模块的方法,称为包(Package),包是模块的集合,比模块又高一级的封装。 没有比包更高级别的封装,但是包可以嵌套包,就像文件目录一样。导入包的方式和导入模块一样,但要注意,包里面必须要有一个init.py 文件,否则会报错。比如在项目 python_one 中,我们创建一个 mytools 包,把 myabs 模块放入该包中,在 hello.py 中使用该包中的 myabsfunc 函数。

如果我们的包中有多个(比如几十个)模块,对每个模块都需要使用 from…import… 的方式导入,那就很 low 了。Python 支持用 from…import * 的方式可以导入init.py 的文件里的all变量里面限定的模块。假如我们的 mytools 包里面有两个模块 myabs 和 myhaha,我们可以在init.py 里面限定要导入的模块。

''' __init__.py 文件。 变量 __all__ 的值是对使用 from...import * 语法导入模块的限定。 ''' __all__ = ["myhaha"] # 如果使用 from...import * 语法,则只导入了 myhaha 模块。
''' myabs.py 文件(该文件为自定义模块 myabs) 。 该模块有一个函数 myabsfunc。 '''defmyabsfunc(data):try:data=int(data)except:raiseTypeError("必须为数字类型")returndataifdata>=0else-data
''' myhaha.py 文件(该文件为自定义模块 myhaha) 。 该模块有一个函数 myhahafunc。 '''defmyhahafunc():return"haha"
''' hello.py 文件。 该文件使用 mytools 包。 '''frommytoolsimport*# 只导入了 __init__.py 文件里的 __all__ 变量里面的模块。print(myhaha.myhahafunc())# 正确print(myabs.myabsfunc(-250))# 错误

当然如果不使用 from…import * 的方式导入模块,则不受init.py 文件里的all变量的限制。 比如我们在init.py 文件里写入__all__ = ["myhaha"],我们可以直接导入 myabs 模块。

''' hello.py 文件。 该文件使用 mytools 包。 '''frommytoolsimportmyabsprint(myabs.myabsfunc(-250))# 正确,不受 __all__ 变量的限制

无论是导入包还是模块还是模块里的函数,变量,或类等等,import 后面的内容必须是一级单位。

''' hello.py 文件。 该文件使用 mytools 包。 '''importmytools# 正确,导入 mytools 包frommytoolsimportmyabs# 正确,导入 mytools 包里的 myabs 模块frommytools.myabsimportmyabsfunc# 正确,导入 mytools 包里的 myabs 模块里的 myabsfunc 函数importmytools.myabs# 错误,import 后面必须是一级单位frommytoolsimportmyabs.myabsfunc# 错误,import 后面必须是一级单位

2️⃣ 模块路径搜索

我们上一节学习如何自定义模块,无论是自定义模块还是内建模块还是第三方模块都是模块,他们的导入方法都是一样的,唯一的区别只是在于他们所在的路径不同。

模块路径搜索规则

大家注意:下面在描述中,我们为了表达方便,把被导入的包,模块,函数,类等等统称为模块, 在导入模块(自定义模块,内建模块,第三方模块具有一样的路径搜索规则)时,不管在程序中执行了多少次 import,该模块只会被导入一次。 这样可以防止程序重复导入模块,节省内存和计算资源。那么,当使用 import… 语句的时候,Python 解释器是怎样找到对应的模块呢,Python 会根据 sys.path 的设置,按顺序搜索模块,我们可以打印出 sys.path。

''' hello.py 文件。 '''importmytools# mytools 包在 sys.path 列表中的路径下frommytoolsimportmyabs# 先找到 mytools 包,再从 mytools 包下找到 myabs 模块frommytools.myabsimportmyabsfunc# 先找到 mytools 包,再从 mytools 包下找到 myabs 模块,再从 myabs 模块里找到 myabsfunc 函数importsys# 内建模块 sys,该模块在 sys.path 列表中的路径下print(sys.path)

我们在 hello.py 文件中打印出了 sys.path 的路径,也就是说我们在该文件中引用的模块必须在这些路径下(直接或通过 from…间接找到),否则就找不到模块。 上面的三种导入方法import mytools,from mytools import myabs,from mytools.myabs import myabsfunc对于 import 导入的模块,我们总是能直接或间接(通过 from…)找到,所以我们在 hello.py 中可以找到我们导入的模块。

但是我们在项目开发过程中有时候,我们的模块所放在的位置有时候并不在 sys.path 列表中,那我们如何导入该模块呢,比如我们不想通过 mytools 间接导入 myabs 模块,我们想直接通过 import 导入 myabs 模块,我们只需要把 myabs 模块所在目录加入到 sys.path 列表中即可。

''' hello.py 文件。 '''importsys sys.path.append("E:\\python_one\\mytools")# 添加字符串 "E:\\python_one\\mytools" 到 sys.path 中print(sys.path)importmyabs# 直接导入 myabs 模块print(myabs.myabsfunc(-250))

用绝对路径的方式在项目开发过程中一般是不允许的,比如我们在本机上开发,项目完成后上传到服务器上, 项目程序所在服务器的盘符和我们本机的盘符不是一个名称(比如我们开发的时候把项目放在本机的 E 盘,而服务器上项目在 C 盘),大家可能会想,我们开放的时候保持和服务器盘符一样不就可以了吗,那如果我们的服务器是 linux 呢?linux 系统没有盘符的概念。我们使用相对路径是解决以上问题最好的方法。

''' hello.py 文件。 '''importsysimportos thisfilepath=os.path.abspath(__file__)# hello.py 文件全路径thisfiledir=os.path.dirname(thisfilepath)# hello.py 所在文件夹全路径mytoolspath=os.path.join(thisfiledir,"mytools")# mytools 文件夹全路径sys.path.append(mytoolspath)# 添加 mytoolspath 所在全路径到 sys.path 中print(sys.path)importmyabs# 直接导入 myabs 模块print(myabs.myabsfunc(-250))

模块路径搜索注意事项

在我们导入模块的时候,有可能出现模块名字重名的情况,大家可能会想,我们约定好给模块起名字不重名不就可以了吗, 但是一个项目中的模块不可能是一个码农写的,总会有这种可能性;还有我们模块名也有可能和内建模块重名;也有 可能和第三方模块重名,这些可能性都有,所以我们要清楚当模块重名的情况下,Python 会按照 sys.path 列表的顺序查找的,当第一次查到后,就不会再继续查找该模块。假定我们创建一个模块 abs 和内建模块重名,我们本意是调用 Python 内置的 abs 函数做绝对值计算,但是 Python 根据 sys.path 列表里面的值,率先找到我们自己写的 abs 模块,就停止了搜索。

''' hello.py 文件。 '''importabsprint(abs(9))# 这时候用的是我们自己模块 abs 而不是 Python 内置的 abs 函数,所以报错。

我们自定义的模块一般放在项目目录下,这是一个好的习惯;内置模块我们安装 Python 解释器的时候,Python 把它放在安装目录下了;第三方模块我们一般自动安装在 Python 安装目录下的 lib 目录下的 site-packages 目录下。

3️⃣ 如何安装第三方模块

在 Python 中,我们安装第三方模块,有三种方式。第一种方式是第三方模块提供一个安装程序, 我们用 Python 解释器去执行这个程序即可;剩下两种方式是通过 setuptools 这个工具完成的。

Python 有两个封装了 setuptools 的包管理工具:easy_install 和 pip,目前官方推荐使用 pip,这两个模块在 Python 3.7 版本 中已经作为内置模块存在了,所以我们不需要安装,即可使用这两个模块。

本节课我们全面学习以上三种方法去安装第三方模块。

Python 直接安装

我们现在以安装第三方模块 six 为例,来详细介绍如何使用 Python 解释器直接安装该模块。 首先我们需要网上下载我们需要安装的模块包, 点击进入 six 模块所在官网,下载源码压缩包到本地然后解压。

在控制台里面进入解压后的 six 模块里 setup.py 所在的文件夹,然后执行 Python setup.py install 即可。

我们知道第三方模块一般安装在 Python 解释器所在安装目录下的 lib 目录下的 site-packages 目录下,通过查看我们发现确实安装上了。

第三方模块的使用方式和内建模块以及自定义模块使用方式一样,我们在安装 six 时,six 自带的 setup.py 程序自动把 six 模块所在路径添加到 sys.path 中了,所以我们直接导入即可。

有时候我们需要安装的第三方模块包并没有提供 setup.py 文件,所以我们无法通过 Python 直接安装,这个时候我们可以使用包管理工具 easy_install 或 pip 方式安装。

使用 easy_install 安装

我们知道在 Python 3.7 版本中,Python 解释器已经把 easy_install 作为 Python 内置模块了,我们可以直接使用该模块,我们使用 easy_install 模块安装第三方模块,不需要自己手动下载第三方模块,我们可以在任意目录下,直接使用 easy_install packageName 来安装第三方模块,easy_install 会自动到模块管理库里面找我们要安装的第三方模块的地址自动下载到本地,然后进行安装。

easy_install 同样会把第三方模块安装到 Python 解释器所在安装目录下的 lib 目录下的 site-packages 目录下,大家可以自行查看,在此就不在赘述,使用方法也一样。

使用 pip 安装

我们知道在 Python 3.7 版本中,Python 解释器已经把 pip 作为 Python 内置模块了,我们可以直接使用 pip 安装第三方模块。我们使用 pip 模块安装第三方模块,不需要自己手动下载第三方模块,我们可以在任意目录下,直接使用 pip install packageName 来安装第三方模块,pip 会自动到模块管理库里面找我们要安装的第三方模块的地址自动下载到本地,然后进行安装。

pip 同样会把第三方模块安装到 Python 解释器所在安装目录下的 lib 目录下的 site-packages 目录下。pip 关于安装路径问题和上面两种方法不同的是:上面两中方法在安装模块的时候,会在 site-packages 目录下创建一个以该模块名加版本号命名的文件夹(包),把所有该模块的类库函数放在在这个包下,然后把这个文件夹的全路径加入到 sys.path 里面; 而用 pip 安装时,则是直接在 site-packages 目录下创建一个以该模块名命名的文件夹(包),把所有该模块的类库函数放在在这个包下,然后再创建一个以该模块名加版本号命名的文件夹放入 该模块的说明信息,因为 site-packages 所在全路径已经在 sys.path 里面,所以我们可以直接使用该模块。

第三方模块大都是外国友人写的,模块所在的服务器也在国外,由于祖国伟大的 Great Firewall 的存在,我们在使用 pip 安装这些模块时,可能导致下载失败无法安装,这时候我们可以使用国内镜像来安装。 使用镜像的安装命令为:pip install -i packageUrl packageName,比如我们使用国内清华大学的镜像安装 six 模块,可以执行命令:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple six。下面表格中我们列出国内主流的镜像地址。

机构名称镜像地址示例(比如用镜像安装 six)
阿里云https://mirrors.aliyun.com/pypi/simplepip install -i https://mirrors.aliyun.com/pypi/simple six
豆瓣https://pypi.douban.compip install -i https://pypi.douban.com six
清华大学https://pypi.tuna.tsinghua.edu.cn/simplepip install -i https://pypi.tuna.tsinghua.edu.cn/simple six
中国科学技术大学https://pypi.mirrors.ustc.edu.cnpip install -i https://pypi.mirrors.ustc.edu.cn six
华中科技大学https://pypi.hustunique.compip install -i https://pypi.hustunique.com six
山东理工大学https://pypi.sdutlinux.orgpip install -i https://pypi.sdutlinux.org six

pip 还有其它功能,比如查看,更新,卸载第三方模块等等,下面我们列出常用的命令。

功能命令示例(下面对更新,移除,搜索操作以 six 模块为例)
查看使用pip已安装的模块pip listpip list
更新第三方模块pip install -U packageNamepip install -U six
移除第三方模块pip uninstall packageNamepip uninstall six
搜索第三方模块pip search packageNamepip search six
查看帮助文档pip helppip help
升级pippip install -U pippip install -U pip

当然我们也可以使用 pip 同时安装多个模块,只需要每个模块用空格隔开即可,比如我们想同时安装 six 模块, pillow 模块和 requests 模块,操作命令如下。

pipinstallsix pillow requests

我们的 pip 维护的数据仓库也不是万全的,有时候有的第三方模块没有地址加入到 pip 的数据仓库中,但是一般模块本身都提供一个后缀名为 whl 的文件,我们可以使用pip install whl文件名或easy_install whl文件名进行安装,下面我们举例使用 pip 安装 whl 文件。

pip 很强大,它本身是包管理工具,用 pip 安装第三方模块时,pip 本身可以根据我们的 Python 版本自动找到适用的第三方模块版本。我们以后要经常使用 pip 来安装第三方模块,几乎用不到前面两种安装方式。

4️⃣ 虚拟环境

Python 的虚拟环境可以使一个 Python 程序拥有独立的库和解释器,而不用与其它 Python 程序共享用一个库。虚拟环境的好处是避免了不同 Python 程序间的互相影响,如果我们不使用虚拟环境,我们在开发项目A时可能需要某个库的 1.0 版本,而我们又需要开发项目 B,而项目 B 需要同样这个库的 2.0 版本,如果程序 B 执行则 A 就不能执行了。

virtualenv 是可以创建和管理虚拟环境的第三方模块,我们可以用该 virtualenv 创建虚拟且独立的 python 运行环境,使得单个项目的运行环境与其它项目独立起来。

安装和使用 virtualenv

首先我们用 pip 来安装 virtualenv,为了防止国外网速慢或下载失败,我们使用使用国内镜像。

pipinstall-ihttps://mirrors.aliyun.com/pypi/simple virtualenv# 使用阿里云镜像安装

我们知道第三方模块一般被安装在 Python 解释器所在的根目录下的 Lib 目录下的 site-packages 目录下,virtualenv 模块同样被安装在该目录下。同时 virtualenv 也提供一个 exe 程序方便我们直接使用,我们以后直接使用该 exe 程序来创建虚拟环境,这个 exe 文件放在 Python 解释器所在的根目录下的 Scripts 目录下。

首先我们进入本地 Python 解释器所在的根目录下的 scripts 目录下, 然后使用命令virtualenv.exe myenv来创建虚拟环境,myenv 是新创建的虚拟环境的名称,该虚拟环境是一个文件夹, 里面是了一个独立的 Python 执行环境。

virtualenv.exe myenv # 创建虚拟环境

该虚拟环境被创建在你控制台执行命令时所在的当前路径下, 因为我们是进入本地 Python 解释器所在的根目录下的 scripts 目录下执行的创建命令,所以虚拟环境 myenv 被创建在该目录(C:\Python3\Scripts)下。

当然我们也可以把虚拟环境创建在其它任何目录下。 有时候为了打包方便,我们会把虚拟环境创建在项目根目录下,比如我们想把虚拟环境创建在E:\python_test项目路径下,此时我们只需要在控制台下进入项目目录下调用 virtualenv 来创建虚拟环境即可,注意要写上:virtualenv.exe 所在的全路径。

项目开发中使用虚拟环境

下面为了方便学习,我们就以上面创建在 C:\Python3\Scripts 路径下的虚拟环境为例。虚拟环境创建好之后,我们就可以在我们的虚拟环境下安装我们的项目需要的第三方模块,在安装第三方模块之前我们要先进入虚拟环境目录,并激活虚拟环境。 激活虚拟环境的命令为:activate,当虚拟环境激活成功,当前路径前面会多出一个虚拟环境的名称。

激活虚拟环境后,我们可以使用 pip 把第三方模块安装到虚拟环境下,下面我们准备把 Pillow 模块安装到虚拟环境下。

我们可以用 deactivate 命令退出我们激活的虚拟环境。

当然,我们开发项目使用的是 Pycharm,比如我们让项目 python_test 使用虚拟环境 myenv,只需要在 Pycharm 下给要开发的项目配置虚拟环境 myenv 下的 Python 解释器即可。

把项目和虚拟环境关联好后,直接运行,我们发现项目调用的是虚拟环境下的解释器。

如果我们想永久废弃虚拟环境 myenv,直接删掉 myenv 文件夹就可以了。

注意事项

大家一定要熟悉 Python 解释器路径的查找规则,查找规则为:当前路径下 Python 解释器 > 虚拟环境所在路径下 Python解释器 > 环境变量路径下 Python 解释器。比如在控制台下激活虚拟环境后,把当前路径切换到 E 盘,然后进入 python 解释器环境,使用代码print sys.executable打印出当前使用的 python 解释器路径。

大家可以做一个试验,把 python.exe 程序拷贝到 E 盘,激活虚拟环境后,把当前路径切换到 E 盘,进入 python 解释器环境,然后打印出当前 python 解释器使用的路径,看看调用的是哪个 Python 解释器。

最后说一句,大家做项目开发中要养成一个好习惯:对每一个项目都关联独立的虚拟环境。

5️⃣ 推荐的项目环境操作

可以使用python -m venv .venv创建项目环境,激活后再运行python -m pip install 包名。这样做能减少系统 Python、多个项目和多个pip命令之间的混淆。项目所需依赖应记录在依赖文件或项目配置中,不要依赖开发机器上碰巧已经安装的包。

🛬 文章小结

  • 模块和包让代码按职责组织;导入能否成功取决于模块搜索路径与当前解释器环境,避免把自定义模块放进解释器安装目录。
  • 使用python -m pip安装、查看和卸载依赖,确认命令对应的解释器;为不同项目分别创建虚拟环境,避免依赖版本相互影响。
  • 可将类与函数放进同一个包供另一脚本导入,再创建两个虚拟环境,分别安装不同版本的 Pillow,核对各环境的依赖列表。
返回列表