在数据科学项目的初期阶段,使用像pip或conda这样的依赖管理工具可能足够了。
然而,随着项目的发展,依赖项的数量增加。这可能使得在pip或conda上有效地重现项目环境并进行有效维护变得困难。
于是,Poetry 登场了,这是一个开源库,为创建和维护具有一致环境的Python项目提供了强大的工具。
在本文中,我们将深入探讨 Poetry 的优势,并详解它与pip和conda的主要区别。
独学而无优则孤陋而寡闻,技术要学会交流、分享,不建议闭门造车。
技术交流与答疑、源码获取,均可加交流群获取,群友已超过2000人,添加时最好的备注方式为:来源+兴趣方向,方便找到志同道合的朋友。
方式①、微信搜索公众号:Python学习与数据挖掘,后台回复:交流
方式②、添加微信号:dkl88194,备注:交流
一键无缝安装过程使得可以迅速将软件包整合到我们的代码库中,节省时间和精力。
1.1 Conda
Conda的安装格式因不同的软件包而异。例如,要安装polars,你需要运行:
conda install -c conda-forge polar
对于pandas,命令是:
conda install -c anaconda pandas
1.2 Pip
Pip为每个软件包维护了一致的安装格式:
pip install package-name
1.3 Poetry
Poetry也遵循相同的安装格式:
poetry add package-name
庞大的软件包选择使得更容易找到最符合需求的特定软件包和版本。
Conda
有些软件包,如snscrape,可能无法通过conda安装。此外,某些版本,如Pandas 2.0,可能无法通过conda获取。
虽然在conda虚拟环境中可以使用pip来解决软件包限制的问题,但conda无法跟踪使用pip安装的依赖项。
$ conda list
# 环境中的软件包:/Users/khuyentran/miniconda3/envs/test-conda
#
# 名称 版本 构建 渠道
Pip
Pip
Pip可以从Python软件包索引(PyPI)和其他仓库安装任何软件包。
Poetry
Poetry也可以从Python软件包索引(PyPI)和其他仓库安装软件包。
减少环境中的依赖项可以简化开发过程。
Conda
Conda提供完整的环境隔离,同时管理Python软件包和系统级依赖项。与其他软件包管理器相比,这可能导致包的大小较大,可能在安装和分发过程中消耗更多的存储空间。
$ conda install pandas
$ conda list
# 环境中的软件包:/Users/khuyentran/miniconda3/envs/test-conda
#
# 名称 版本 构建 渠道
blas 1.0 openblas
bottleneck 1.3.5 py311ha0d4635_0
bzip2 1.0.8 h620ffc9_4
# ... (省略部分输出)
pandas 1.5.3 py311h6956b77_0
pip 23.0.1 py311hca03da5_0
python 3.11.3 hb885b13_1
# ... (省略部分输出)
Pip
Pip只安装包所需的依赖项。
$ pip install pandas
$ pip list
Package Version
--------------- -------
numpy 1.24.3
pandas 2.0.2
pip 22.3.1
python-dateutil 2.8.2
pytz 2023.3
setuptools 65.5.0
six 1.16.0
tzdata 2023.3
Poetry
Poetry同样只安装包所需的依赖项。
$ poetry add pandas
$ poetry show
# ... (省略部分输出)
卸载软件包及其依赖项可以释放磁盘空间,防止不必要的混乱,并优化存储资源的使用。
Pip
Pip仅移除指定的软件包,而不包括其依赖项,这可能导致随着时间的推移未使用依赖项的积累。这可能导致存储空间使用增加和潜在的冲突。
$ pip install pandas
$ pip uninstall pandas
$ pip list
Package Version
--------------- -------
numpy 1.24.3
pip 22.0.4
python-dateutil 2.8.2
pytz 2023.3
setuptools 56.0.0
six 1.16.0
tzdata 2023.3
Conda
Conda移除软件包及其依赖项。
$ conda install -c conda pandas
$ conda uninstall -c conda pandas
Collecting package metadata (repodata.json): done
Solving environment: done
## Package Plan ##
environment location: /Users/khuyentran/miniconda3/envs/test-conda
removed specs:
- pandas
The following packages will be REMOVED:
blas-1.0-openblas
bottleneck-1.3.5-py311ha0d4635_0
libcxx-14.0.6-h848a8c0_0
libgfortran-5.0.0-11_3_0_hca03da5_28
libgfortran5-11.3.0-h009349e_28
libopenblas-0.3.21-h269037a_0
llvm-openmp-14.0.6-hc6e5704_0
numexpr-2.8.4-py311h6dc990b_1
numpy-1.24.3-py311hb57d4eb_0
numpy-base-1.24.3-py311h1d85a46_0
pandas-1.5.3-py311h6956b77_0
python-dateutil-2.8.2-pyhd3eb1b0_0
pytz-2022.7-py311hca03da5_0
six-1.16.0-pyhd3eb1b0_1
Proceed ([y]/n)?
Preparing transaction: done
Verifying transaction: done
Executing transaction: done
Poetry
Poetry也可以移除软件包及其依赖项。
$ poetry add pandas
$ poetry remove pandas
? Removing numpy (1.24.3)
? Removing pandas (2.0.2)
? Removing python-dateutil (2.8.2)
? Removing pytz (2023.3)
? Removing six (1.16.0)
? Removing tzdata (2023.3)
依赖文件通过指定所需软件包的确切版本或版本范围,确保了软件项目环境的可复制性。这有助于在不同系统或不同时间点上重新创建相同的环境,确保具有相同依赖关系的开发者之间的协作。
Conda
要保存Conda环境中的依赖项,您需要手动将它们写入文件。在environment.yml文件中指定的版本范围可能导致安装不同版本,从而在复制环境时引入兼容性问题。
假设已安装pandas版本1.5.3。以下是一个带有指定依赖项的示例environment.yml文件:
# environment.yml
name: test-conda
channels:
- defaults
dependencies:
- python=3.8
- pandas>=1.5
如果新用户尝试在pandas最新版本为2.0时重新创建环境,将安装pandas 2.0而不是1.5.3。
# 创建并激活虚拟环境
$ conda env create -n env
$ conda activate env
# 列出当前环境中的软件包
$ conda list
...
pandas 2.0
如果代码库依赖于特定于pandas 1.5.3的语法或行为,并且在版本2.0中发生了更改,则使用pandas 2.0运行代码可能导致错误。
Pip
使用pip也可能出现类似的问题。
# Requirements.txt
pandas>=1.5
# 创建并激活虚拟环境
$ python3 -m venv venv
$ source venv/bin/activate
# 安装依赖项
$ pip install -r requirements.txt
# 列出软件包
$ pip list
Package Version
---------- -------
pandas 2.0
...
...
您可以在requirements.txt文件中指定版本:
$ pip freeze > requirements.txt
# requirements.txt
numpy==1.24.3
pandas==1.5.3
python-dateutil==2.8.2
pytz==2023.3
然而,这样的环境不够灵活,长期维护可能更具挑战性。对依赖项进行任何更改都需要手动修改requirements.txt文件,这可能是耗时且容易出错的。
Poetry
Poetry在安装软件包时会自动更新pyproject.toml文件。
在下面的示例中,使用版本约束1.5添加了’pandas’软件包。这种灵活的版本管理确保您的项目可以适应更新的版本而无需手动调整。
$ poetry add 'pandas=^1.5'
# pyproject.toml
[tool.poetry.dependencies]
python = "^3.8"
pandas = "^1.5"
poetry.lock文件存储了每个软件包及其依赖项的确切版本号。
# poetry.lock
...
[[package]]
name = "pandas"
version = "1.5.3"
description = "Powerful data structures for data analysis, time series, and statistics"
category = "main"
optional = false
python-versions = ">=3.8"
[package.dependencies]
numpy = [
{version = ">=1.20.3", markers = "python_version < \"3.10\""},
{version = ">=1.21.0", markers = "python_version >= \"3.10\""},
{version = ">=1.23.2", markers = "python_version >= \"3.11\""},
]
python-dateutil = ">=2.8.2"
pytz = ">=2020.1"
tzdata = ">=2022.1"
...
这确保了即使在pyproject.toml文件中指定了版本范围,也能保持一致性。在这里,我们看到安装了pandas 1.5.3而不是2.0。
$ poetry install
$ poetry show pandas
name : pandas
version : 1.5.3
description : Powerful data structures for data analysis, time series, and statistics
dependencies
- numpy >=1.20.3
- numpy >=1.21.0
- numpy >=1.23.2
- python-dateutil >=2.8.1
- pytz >=2020.1
通过分离依赖项,可以清晰地区分用于开发目的的软件包(如测试框架和代码质量工具)和生产环境所需的软件包,后者通常包括核心依赖项。
这确保生产环境仅包含运行应用程序所需的软件包,降低冲突或兼容性问题的风险。
Conda
Conda本身并不直接支持不同环境的独立依赖项,但可以通过创建两个环境文件来实现:一个用于开发环境,一个用于生产环境。开发文件包含生产和开发两者的依赖项。
# environment.yml
name: test-conda
channels:
- defaults
dependencies:
# Production packages
- numpy
- pandas
# environment-dev.yml
name: test-conda-dev
channels:
- defaults
dependencies:
# Production packages
- numpy
- pandas
# Development packages
- pytest
- pre-commit
Pip
Pip也不直接支持独立的依赖关系,但可以使用单独的requirements文件采用类似的方法。
# requirements.txt
numpy
pandas
# requirements-dev.txt
-r requirements.txt
pytest
pre-commit
# 安装生产依赖项
$ pip install -r requirements.txt
# 安装开发和生产依赖项
$ pip install -r requirements-dev.txt
Poetry
Poetry通过在单个文件中支持分组来简化依赖关系管理。这允许您在一个地方跟踪所有依赖关系。
$ poetry add numpy pandas
$ poetry add --group dev pytest pre-commit
# pyproject.toml
[tool.poetry.dependencies]
python = "^3.8"
pandas = "^2.0"
numpy = "^1.24.3"
[tool.poetry.group.dev.dependencies]
pytest = "^7.3.2"
pre-commit = "^3.3.2"
要仅安装生产依赖项:
$ poetry install --only main
要安装开发和生产依赖项:
$ poetry install
更新依赖项对于享受新版软件包中引入的错误修复、性能改进和新功能至关重要。
Conda
Conda允许更新特定软件包:
$ conda install -c conda pandas
$ conda install -c anaconda scikit-learn
# 新版本可用
$ conda update pandas
$ conda update scikit-learn
之后,您需要手动更新environment.yaml文件,以使其与更新的依赖项保持同步:
$ conda env export > environment.yml
Pip
Pip也允许更新特定软件包,需要手动更新requirements.txt文件:
$ pip install -U pandas
$ pip freeze > requirements.txt
Poetry
使用Poetry,您可以使用update命令升级在pyproject.toml文件中指定的所有软件包。此操作会自动更新poetry.lock文件,确保软件包规范与锁定文件之间的一致性:
$ poetry add pandas scikit-learn
# 新版本可用
$ poetry update
更新依赖项
解析依赖关系… (0.3秒)
编写锁文件
软件包操作: 0 安装,2 更新,0 删除
? 更新 pandas (2.0.0 -> 2.0.2)
? 更新 scikit-learn (1.2.0 -> 1.2.2)
依赖冲突发生在项目所需的软件包或库的版本存在冲突或不兼容依赖关系时。正确解决冲突对于避免错误、运行时问题或项目失败至关重要。
Conda
Conda使用SAT求解器来探索所有软件包版本和依赖关系的组合,以找到一个兼容的集合。
例如,如果现有软件包对其依赖项有特定的约束(例如,statsmodels==0.13.2需要numpy>=1.21.2,<2.0a0),而您要安装的软件包不符合这些要求(例如,numpy<1.21.2),Conda不会立即抛出错误。相反,它会认真搜索所有所需软件包及其依赖项的兼容版本,只有在找不到合适的解决方案时才报告错误。
$ conda install 'statsmodels==0.13.2'
$ conda search 'statsmodels==0.13.2' --info
此方法增加了找到解决方案的机会,但在处理复杂环境时可能需要大量计算资源。
Pip
Pip按顺序安装软件包,这意味着它按照指定的顺序逐个安装每个软件包。这种顺序方法有时可能导致冲突,特别是当软件包存在不兼容的依赖关系或版本要求时。
例如,假设您最初安装pandas2.0.2,它要求numpy>=1.20.3。然后,您使用pip安装numpy1.20.2。尽管这导致依赖冲突,pip仍会继续更新numpy版本。
$ pip install pandas==2.0.2
$ pip install numpy==1.22.2
Poetry
通过关注项目的直接依赖关系,Poetry的确定性解析器缩小了搜索空间,使解决过程更加高效。它评估指定的约束,例如版本范围或特定版本,并迅速识别任何冲突。
$ poetry add 'seaborn==0.12.2'
$ poetry add 'matplotlib<3.1'
这种即时的反馈有助于防止潜在的升级问题,并允许开发人员在开发过程的早期解决问题。例如,在下面的代码中,我们可以放宽对seaborn的要求,以安装特定版本的matplotlib:
poetry add 'seaborn<=0.12.2' 'matplotlib<3.1'
Package operations: 1 install, 2 updates, 4 removals
? Removing contourpy (1.0.7)
? Removing fonttools (4.40.0)
? Removing packaging (23.1)
? Removing pillow (9.5.0)
? Updating matplotlib (3.7.1 -> 3.0.3)
? Installing scipy (1.9.3)
? Updating seaborn (0.12.2 -> 0.11.2)
总之,Poetry相对于pip和conda具有以下几个优势: