Python 包的依赖管理,最强解释!

2023年 7月 11日 127.3k 0

可是稍微了解了一下后发现其实 Python 不止能当脚本语言来用。基于一定的工具链,Python 也能写出漂亮标准的项目代码、将环境和依赖理的明明白白。

基于PIP

最基础的依赖管理应当能解决如下问题:

  • 能快速配置好项目依赖,搭建好开发环境。
  • 明确知道当前项目依赖了哪些第三方的包,以及他们的依赖树。
  • 能快速添加和移除给定的依赖,进行依赖调解。
  • 这些功能使用 Pip 工具链其实是能很方便做到的。

    快速配置环境(pip)

    想简单预览当前环境下的依赖包可以直接用 pip list 命令:

    $ pip list
    Package    Version
    ---------- -------------------
    certifi    2020.6.20
    pip        19.3.1
    setuptools 44.0.0.post20200106
    wheel      0.36.2
    

    对于一个空的 Python 环境,基础一般只会有这四个包。我们这样就知道了当前环境中有哪些包,以及他们的版本。

    为了方便说明,我们先多引一些依赖 pip install flask 。

    $ pip list
    Package      Version
    ------------ -------------------
    certifi      2020.6.20
    click        7.1.2
    Flask        1.1.2
    itsdangerous 1.1.0
    Jinja2       2.11.3
    MarkupSafe   1.1.1
    pip          19.3.1
    setuptools   44.0.0.post20200106
    Werkzeug     1.0.1
    wheel        0.36.2
    

    安装了 Flask 之后,我们发现除了 Flask 他还多引入了好多个间接依赖。

    如果想要将这个信息记录下来,我们可以用 pip freeze 命令,记在 requirements.txt 中(一个约定俗成的名字)。

    $ pip freeze > requirements.txt
    $ cat requirements.txt
    certifi==2020.6.20
    click==7.1.2
    Flask==1.1.2
    itsdangerous==1.1.0
    Jinja2==2.11.3
    MarkupSafe==1.1.1
    Werkzeug==1.0.1
    

    好了,记下这个文件,以后我们如果需要在一个新的 Python 环境中引入当前的依赖,只需要使用 pip install -r requirements.txt 即可。

    明确项目依赖(pipdeptree)

    pip list 或 pip freeze 打印出来的依赖有一个问题,就是并没有明确依赖关系。这样的坏处是,当我们想清理依赖的时候,就不知道到底哪些依赖是能被直接删除的、哪些依赖又是被间接依赖而不能轻易删除的。

    例如我们可能在项目中用了 Flask ,但是我们可能不知道 Flask 也引用了 Jinja2 。这是我们如果擅自删除了 Jinja2 ,项目就可能跑不起来。。。

    这时就可以使用 pipdeptree 工具来管理依赖树:

    $ pip install pipdeptree
    ...
    $ pipdeptree
    certifi==2020.6.20
    Flask==1.1.2
      - click [required: >=5.1, installed: 7.1.2]
      - itsdangerous [required: >=0.24, installed: 1.1.0]
      - Jinja2 [required: >=2.10.1, installed: 2.11.3]
        - MarkupSafe [required: >=0.23, installed: 1.1.1]
      - Werkzeug [required: >=0.15, installed: 1.0.1]
    pipdeptree==2.0.0
      - pip [required: >=6.0.0, installed: 19.3.1]
    setuptools==44.0.0.post20200106
    wheel==0.36.2
    

    现在我们就知道了,原来 Jinja2 是被 Flask 依赖的,这样我们就不会随便删除了。。。

    项目依赖治理(pip-autoremove)

    那么问题来了,如果我忽然不想依赖 Flask 了,我们需要怎么做呢?

    无脑的做法是 pip uninstall flask -y 。不那么显然的是,这其实不够优雅:

    $ pip uninstall flask -y
    ...
    $ pipdeptree
    certifi==2020.6.20
    click==7.1.2
    itsdangerous==1.1.0
    Jinja2==2.11.3
      - MarkupSafe [required: >=0.23, installed: 1.1.1]
    pipdeptree==2.0.0
      - pip [required: >=6.0.0, installed: 19.3.1]
    setuptools==44.0.0.post20200106
    Werkzeug==1.0.1
    wheel==0.36.2
    

    发现没,Flask 虽然被卸载了,但是他的依赖包并没有卸载干净。你可能需要重新一个一个判断你是否需要剩下的包,然后再递归删除。。。

    幸运的是,我们就可以用 pip-autoremove 工具来做这件事。我们重新安装Flask,再用这个工具删除试试:

    $ pip install flask
    $ pip install pip-autoremove
    $ pip-autoremove flask -y
    $ pipdeptree
    certifi==2020.6.20
    pip-autoremove==0.9.1
    pipdeptree==2.0.0
      - pip [required: >=6.0.0, installed: 19.3.1]
    setuptools==44.0.0.post20200106
    wheel==0.36.2
    

    这下干净了?。

    基于Conda

    pip 能基本解决单一项目的环境处理问题。但是由于 Python 是全局环境,如果有多个项目,我们就无法区分项目维度的依赖。解决这个问题一般有两个思路,一个是像 Node.js 一样用 package.json 配置文件支持项目维度的环境隔离,另一个就是走 rvm、nvm的思路用虚拟环境隔离。目前看 Python 只能支持后者,也就是用基于 Conda 的虚拟环境。

    值得一提的是,conda 虽然为Python 而生,但他其实是一个通用的虚拟环境工具。他的官网写的很清楚:

    Package, dependency and environment management for any language---Python, R, Ruby, Lua, Scala, Java, JavaScript, C/ C++, FORTRAN

    Conda is an open-source package management system and environment management system that runs on Windows, macOS, and Linux. Conda quickly installs, runs, and updates packages and their dependencies. Conda easily creates, saves, loads, and switches between environments on your local computer. It was created for Python programs but it can package and distribute software for any language.

    很强大,有多强大,可以将不同语言的依赖环境整合在一起的强大。

    安装

    Conda 官网给了两个发行版本,一个是 Anaconda ,一个是 Miniconda。Anaconda 相比 Miniconda 主要是多预装了很多科学计算的库,而我更喜欢按需使用不喜欢全家桶,所以我选 Miniconda。

    官网下载miniconda3,并执行安装脚本。

    安装后会发现 .bashrc 下多了几行:

    # >>> conda initialize >>>
    # !! Contents within this block are managed by 'conda init' !!
    __conda_setup="$('/home/zhenping/miniconda3/bin/conda' 'shell.bash' 'hook' 2> /dev/null)"
    if [ $? -eq 0 ]; then
        eval "$__conda_setup"
    else
        if [ -f "/home/zhenping/miniconda3/etc/profile.d/conda.sh" ]; then
            . "/home/zhenping/miniconda3/etc/profile.d/conda.sh"
        else
            export PATH="/home/zhenping/miniconda3/bin:$PATH"
        fi
    fi
    unset __conda_setup

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论