# 库
如果想将 Python 模块、包分享给其他用户使用,则通常封装成代码库(Library)的形式,加上作者、版本号等描述信息。
- 一个代码库,可能很简单,只包含一个 Python 模块。也可能很复杂,包含几十个 Python 模块。
- 一个 Python 库,除了包含 Python 脚本文件,还可能包含一些二进制文件。从广义上来说,一个 Python 库,就属于一个软件包。
- 本文提到的 Python 包,不是指包目录,而是指软件包。
Python 与其它编程语言相比的一大特色是,官方、社区提供了大量库,实现了丰富的功能。安装这些库,就不必自己重复造轮子。
- Python 官方提供了一些标准库,大部分在安装 Python 解释器时就自带了,也可以通过 pip 命令下载它们。
- 用户还可以使用其他用户发布的 Python 库,称为第三方库。
- 目前流行的第三方库分享平台是 https://pypi.org/ (opens new window)
- 第三方库一般安装在 site-packages 目录下,比如
/usr/lib/Python3.9/site-packages/
。可执行命令python -m site
查看 site-packages 目录的绝对路径。
# import setuptools
:Python 的标准库,用于制作 Python 软件包。
- Python 软件包的根目录下,通常存在一个 setup.py 文件,用于打包或安装该库。
python setup.py build # 编译(不一定需要) python setup.py install # 安装
- easy_install 是 setuptools 库提供的一个命令行工具,用于安装 Python 软件包。比较老旧,不推荐使用。
# 打包格式
Python 软件包通常包含几种文件:
- .py 文件:只包含 Python 源代码,通常可以跨平台运行。
- .pyd、.so 等二进制文件:通常不能跨平台运行。
- .pyd 文件是用其它编程语言生成的扩展模块,可以被 Python 导入。
- .json、.yaml 等配置文件
一个 Python 软件包通常包含几十、几百个文件,为了方便网络传输,通常打包成一个压缩文件。分为两种打包格式:
- 源代码包
- :采用 tar、zip 等压缩格式。只包含源文件,不包含预编译文件,因此不能直接使用,需要用户解压后,运行其中的 setup.py 文件进行编译、安装。
- 属于源代码发布(Source Distribution)。
- 可以添加一个 MANIFEST.in 文件,声明打包时需要 include 或 exclude 的文件。
- 如果一个 Python 软件包,纯由 py 文件组成,则源代码包可以直接使用。否则,源代码包通常需要编译,然后才能使用。
- wheel 包
- :采用 zip 压缩格式,扩展名为 .whl ,包含预编译文件,因此用户可以直接使用。
- 属于二进制发布(Binary Distribution)。
- 默认不会读取 MANIFEST.in 文件。
- 一个包含预编译文件的 Python 库,通常需要对不同平台分别编译 wheel 包,按以下格式命名:
{distribution}-{version}-{python tag}(-{build tag})?-{abi tag}-{platform tag}.whl # distribution :库名 # version :版本 # build tag :内部版本号,可选 # python tag :支持的 Python 解释器,比如 py2、py3、py27、py35、cp35 ,取值为 none 则不限制 # abi tag :要求的 Python 解释器的 ABI ,取值为 none 则不限制 # platform tag :支持的平台,比如 manylinux1_x86_64 ,取值为 any 则不限制
- 包名中的各个字段只能包含字母、数字,其它字符都要转换成下划线
_
。 - wheel 包名示例:
Django-3.1.5-py3-none-any.whl # 它表示 Django 库的 3.1.5 版本,只支持 Python3 解释器,支持所有平台 numpy-1.19.5-cp36-cp36m-manylinux1_i686.whl PyQt6-6.0.0-6.0.0-cp36.cp37.cp38.cp39-abi3-macosx_10_15_aarch64.whl
- 包名中的各个字段只能包含字母、数字,其它字符都要转换成下划线
# 打包步骤
通常通过 setup.py 文件制作源代码包或 wheel 包,步骤如下:
先编写一个 setup.py 文件,配置该 Python 软件包:
import setuptools with open('README.md', encoding='utf-8') as f: readme_md = f.read() setuptools.setup( name='pyexiv2', # 该库的名称。由 ASCII 字母、数字组成,不区分大小写。还可包含 -_. 三种字符,但视作相同 version='2.1.0', # 该库的版本 author='LeoHsiao', author_email='[email protected]', description='Read/Write metadata of digital image, including EXIF, IPTC, XMP.', # 简介 long_description=readme_md, # 详细说明 long_description_content_type='text/markdown', license='GPLv3', url='https://github.com/LeoHsiao1/pyexiv2', # 项目网站 packages=setuptools.find_packages(), # 从当前目录开始,自动发现包以及子包,加入打包 # packages=['pyexiv2','pyexiv2/tests'], # 指定一些目录(不会包括子目录),加入打包 package_data={'': ['*.md', '*/*.md']}, # 将某个包中某些路径的文件加入打包,包名为空则匹配所有包 python_requires='>=3.5', # 安装该库需要的 Python # install_requires=["pybind11==2.4.3"], # 安装该库需要的依赖库 classifiers=[ # 该库的分类标签 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', 'Operating System :: POSIX :: Linux', 'Operating System :: MacOS', 'Operating System :: Microsoft :: Windows', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', ], )
- Python2 通常基于标准库
distutils
编写 setup.py 文件。 - Python3 通常基于第三方库
setuptools
编写 setup.py 文件。
- Python2 通常基于标准库
然后执行以下命令进行打包:
python -m pip install setuptools wheel twine # 安装打包工具 git clean -d -fx # 删掉项目目录下的多余文件 python setup.py sdist # 生成源代码包,默认保存到 dist 目录下 python setup.py bdist_wheel # 生成 wheel 包 --python-tag py3 # 指定 Python 解释器标签 --py-limited-api cp32 # 指定 ABI 标签 --plat-name win-amd64 # 指定平台标签
可以将 dist 目录下的 Python 软件包,上传到 pypi.org :
twine upload dist/*
- 每次执行 twine 时都需要在终端输入用户名、密码。如果嫌麻烦,用户可将凭据保存在
~/.pypirc
文件中,可保存多个仓库的凭证:然后在上传包时指定仓库:[distutils] index-servers= pypi testpypi [pypi] repository: https://upload.pypi.org/legacy/ username: *** password: *** [testpypi] repository: https://test.pypi.org/legacy/ username: *** password: ***
twine upload dist/* --repository pypi
- 不支持上传平台标签为 linux 的 wheel 包,需要改成 manylinux ,强调该 wheel 包兼容不同的 Linux 发行版。
- 比如 manylinux2010 是指大概于 2010 年之后发布的 Linux 发行版,支持 x86_64 和 i686 的 CPU 架构。
- 上传某个版本的 wheel 包之后,不能重复上传或修改该版本的 wheel 包,只能上传更高版本的 wheel 包。
- 每次执行 twine 时都需要在终端输入用户名、密码。如果嫌麻烦,用户可将凭据保存在
# import pip
:Python 的第三方库。相当于改进版的 easy_install ,是目前流行的 Python 软件包管理工具,可以下载、安装、卸载 Python 软件包。
# 安装
- 在 Window 上,官方的 Python 安装包自带了 pip 工具。
- 在 Linux 上,如果从源代码编译 Python ,则自带了 pip 工具。
- 在 Linux 上,如果用 yum、apt 安装预编译的 Python ,则可能缺少 pip ,可执行官方的安装脚本:这样默认是安装到全局,也可以单独给当前用户安装:
wget https://bootstrap.pypa.io/get-pip.py python get-pip.py rm -f get-pip.py pip install --upgrade setuptools
python get-pip.py --user echo 'export PATH=$PATH:~/.local/bin' >> ~/.bash_profile source ~/.bash_profile
# 命令
pip
install <name>... # 安装指定的包
--upgrade # 安装最新的版本
--user # 安装到当前用户的 ~/.local/ 目录下。否则默认是安装到 Python 解释器的安装目录下,供所有用户访问
--no-cache-dir # 下载时禁用缓存。默认会将下载的包缓存到磁盘
-i https://pypi.python.org/simple # --index-url ,指定远程仓库的索引页面,默认采用 pypi 。可改用网速更快的国内镜像源,比如 https://pypi.tuna.tsinghua.edu.cn/simple
--timeout 15 # 设置访问远程仓库的超时时间
--proxy [user:passwd@]proxy.server:port # 使用一个网络代理。使用 socks 代理时需要先 pip install PySocks
uninstall <name>... # 卸载指定的包
show <name>... # 显示已安装包的信息
list # 显示已安装的所有包
-V # 显示版本(还会显示该 pip 属于哪个 Python 解释器)
cache
dir # 显示缓存的保存目录
info # 显示缓存的信息
list [pattern] # 列出缓存的包
remove <pattern> # 删除缓存的包
purge # 删除所有缓存的包
- 执行
pip install <name>
时,- name 可以是一个 Python 软件包的名称,比如 'pip install django' 。
- 还可以加上版本号,比如 'django==2.2.0' 、'django>=2.2.0' 。
- 如果该包已安装,则 pip 会结束执行,显示相应提示,比如
Requirement already satisfied: django
。 - 如果该包未安装,则 pip 会从远程仓库搜索并下载它,然后安装。
- 如果该包需要依赖其它包,则 pip 会自动安装它们。
- name 也可以是本机上的 Python 软件包的文件路径。比如
pip install /tmp/django-2.2.0-py3-none-any.whl
。 - 如果远程仓库需要身份认证,则每次执行 pip install 时都需要在终端输入用户名、密码。如果嫌麻烦,用户可将凭据保存在
~/.netrc
文件中:machine nexus.ops.test.com login root password ******
- name 可以是一个 Python 软件包的名称,比如 'pip install django' 。
- pip 本身也是一个 Python 的第三方库,会安装到某个 Python 解释器的 site-packages 目录下,专属于这个 Python 解释器。
- 安装了多个 Python 解释器时,注意区分它们的 pip ,可以通过执行
python -m pip
来指定某个 Python 解释器的 pip 。 - 可以让 pip 更新自己:
pip install --upgrade pip
- 安装了多个 Python 解释器时,注意区分它们的 pip ,可以通过执行
- 可执行以下命令,同步 pip 安装的所有包:不过这样并不能解决依赖库冲突的问题:假设安装包 A 时需要安装包
pip freeze > requirements.txt # 将 pip 已安装的所有包及其版本导出到文本文件中 pip install -r requirements.txt # 读取文件文件的内容,安装其中列出的所有包
B==2.0
、C==3.0
,而安装库 B 时又自动安装了包C==3.1
,则包 A 即使安装成功,也可能运行出错。
# import virtualenv
:Python 的第三方库,用于创建 Python 的虚拟环境。
安装:
pip install virtualenv
为什么要使用虚拟环境?
- 使用 pip 安装一个第三方库时,默认会安装到本机系统的 site-packages 目录,供所有用户共用。缺点如下:
- 系统的 site-packages 目录需要 root 权限才能写入,普通用户无权修改。
- 多个 Python 项目的第三方库,安装在同一个 site-packages 目录中,可能发生冲突。
- 因此,建议为每个 Python 项目创建一个 Python 虚拟环境,分别使用一个 site-packages 目录及配套设施,从而隔离它们的运行环境。
- 使用 pip 安装一个第三方库时,默认会安装到本机系统的 site-packages 目录,供所有用户共用。缺点如下:
用法:
- 创建虚拟环境:它会创建一个名为 .venv 的文件夹,将 Python 解释器的 bin、lib 目录拷贝进去。
virtualenv .venv -p /usr/bin/python3.9 # 指定要拷贝的 Python 解释器(默认是安装 virtualenv 的那个 Python 解释器)
- Python 的可执行文件只是拷贝软链接。
- 默认不会拷贝第三方库,只会拷贝 pip、setuptools、wheel 三个包。
- 使用虚拟环境中的 Python 解释器:也可以进入虚拟环境的 Shell :
.venv/bin/python
[root@CentOS ~]# source .venv/bin/activate (.venv) [root@CentOS ~]# type python python is hashed (/root/.venv/bin/python) (.venv) [root@CentOS ~]# deactivate # 退出虚拟环境的 Shell (如果采用其它方式退出,则可能不能恢复之前的 shell )
- 创建虚拟环境:
# import pipenv
:Python 的第三方库,兼有 pip 和 virtualenv 的功能,更方便。
- 官网 (opens new window)
- 安装:
pip install pipenv
# 原理
- 使用 pipenv 时,它会调用 virtualenv 为当前项目创建一个 Python 虚拟环境。
- 默认是在用户家目录下创建一个目录来保存虚拟环境,比如
/home/leo/.local/share/virtualenvs/test-BuDEOXnJ
,该目录的命名规则为 "项目目录名-哈希值" 。- 可以声明环境变量
export PIPENV_VENV_IN_PROJECT=1
,让 pipenv 将虚拟环境保存在${project_dir}/.venv
目录下。
- 可以声明环境变量
- pipenv 会记录每个项目目录与虚拟环境目录的对应关系。
- 如果改变项目目录的路径,则 pipenv 会找不到该项目对应的虚拟环境目录。
- 每个项目目录同时只能分配一个虚拟环境目录。如果多次创建虚拟环境,则会移除之前的虚拟环境目录。
- 默认是在用户家目录下创建一个目录来保存虚拟环境,比如
- 执行
pipenv install
或pipenv uninstall
时会自动更新 Pipfile 和 Pipfile.lock 文件,如果它们不存在则自动创建。- Pipfile 记录了用户执行
pipenv install
时指定的 Python 软件包的名称、版本、远程仓库,用于代替 pip 生成的 requirements.txt 文件。 - Pipfile.lock 记录了实际安装的所有 Python 软件包,比 Pipfile 多记录了哈希值、依赖关系。
- 例如用户执行
pipenv install requests
,Pipfile 中记录的是requests==*
,Pipfile.lock 中记录的是实际安装的版本。
- 例如用户执行
- Pipfile 记录了用户执行
# 命令
pipenv
--python 3.9 # 创建虚拟环境,使用指定版本的 Python 解释器(它必须已安装)
--python /path/to/python # 使用指定路径的 Python 解释器
--where # 显示项目的根目录,即 Pipfile 所在目录
--venv # 显示虚拟环境所在目录
--rm # 删除虚拟环境
--envs # 显示 pipenv 支持的所有环境变量
install # 安装 Pipfile 中的所有包
<name>... # 安装指定的包(兼容 pip install 的语法)
uninstall
<name>... # 卸载指定的包
--all # 卸载所有包
graph # 以树形结构显示当前虚拟环境安装的所有包
check # 检查 Pipfile 是否存在格式错误、安全漏洞
lock # 生成 Pipfile.lock 文件
sync # 安装 Pipfile.lock 指定的所有包
clean # 卸载 Pipfile.lock 之外的所有包(这不会修改 Pipfile.lock )
update # 执行 lock 和 sync
shell # 进入虚拟环境的 bash shell 。这会导入相应的环境变量,执行 exit 可退出
run <command> # 在虚拟环境的 shell 中执行一条命令
- 例:创建虚拟环境
PIPENV_VENV_IN_PROJECT=1 pipenv --python 3.9 pipenv shell