【问题标题】:Can I import a package installed in one virtual environment in global or another virtual environment?我可以导入安装在全局或另一个虚拟环境中的一个虚拟环境中的包吗?
【发布时间】:2020-06-28 17:57:11
【问题描述】:

我想在我的电脑上安装两个版本的 TensorFlow (tf)。我的一些程序在 tf v1 上运行,一些在 tf v2 上运行。因此,我可以分别创建两个虚拟环境 (venv),一个用于 tf v1,另一个用于 tf v2,并为每个 venv 使用 --use-system-packages 来满足我的代码的其他(全局)包要求。然后,我会将我的程序保存在安装了正确 tf 版本的相应 venv 中。

但是,出于某些特定原因,我也希望能够在全局环境中使用这两个版本的 TensorFlow(即无需输入/激活 venv)。

问题

有没有一种方法可以让我在 2 个 venv 中安装 2 个版本的 TensorFlow(和/或 numpy),然后使用 import 语句在全局环境中访问它们中的任何一个(使用要导入的版本 / venv指定的)?如果使用 venvs 无法做到这一点,有没有其他方法可以达到同样的效果?

进一步扩展相同的内容,假设我在一个 venv 中。我想知道我是否可以导入安装在其他 venv 中的包?如果是,怎么做?

【问题讨论】:

  • 简短的回答是:你不能,更重要的是,你不应该。如果您对为什么会这样的血腥细节感兴趣,请告诉我,我会写一个完整的答案。
  • @Marat 谢谢,我对细节很感兴趣,如果您能抽出时间写一个完整的答案,那就太好了!在过去的 2-3 个小时里,我一直在搜索和阅读它,但找不到任何相关的东西......

标签: python tensorflow virtualenv


【解决方案1】:

问题定义: 我们需要导入两个不同版本的 Python 包。两个版本都有依赖关系并提供相同的命名空间 (tensorflow)。

为什么不可能:当 Python 加载模块 (import tensorflow as tf) 时,它会尝试避免冗余工作并跳过已经加载的模块。 因此,不可能从两个不同的位置加载同一个命名空间。

一些有趣的事实:

  • 您可以操作 Python 在运行时查找模块的路径。第一次加载模块时,Python 会遍历sys.path 中的目录列表,寻找请求的命名空间。 sys.path 中的目录依赖于安装,但通常按以下顺序排列:本地目录、内置模块、已安装的包。最后一个是您的包管理器(pip、easy_install、...)存储已安装包的目录或一组目录。它通常不止一个位置,包括安装在系统范围内的软件包、用户文件夹(例如~/.local/lib/pythonX.Y/site-packages)和特定于虚拟环境的文件夹(例如xxx.venv/lib/pythonX.Y/site-packages)。根据设置,您可以有这些的任意组合。有趣的是您可以插入新目录:sys.path[6:6] = ['path/to/another/venv/lib/pythonX.Y/site-packages']。但是,由于缓存,它不会帮助获取相同命名空间的两个实例。
  • sys.path 文件夹中,Python 将查找具有匹配名称的模块,该名称代表一个命名空间。 Python 包名通常与它们提供的命名空间相同,但也可以不同。例如,在pip install numpy 中,最后一部分是包名,但在import numpy 中,它是命名空间。大约 1/3 的 PyPI 包的命名空间与包名不同,例如包intel-numpy 也提供命名空间numpy。如果你安装了intel-numpy,pip 会默默地覆盖之前由numpy 提供的文件夹(以及命名空间的内容)。是的,Python 包管理器跟踪已安装的包名称,但不跟踪命名空间。因此,即使一个包的两个版本提供不同的命名空间,您也不能同时拥有它们。但是,如果您有两个不同的包提供相同的命名空间,pip 会很乐意同时安装这两个包,但只会保留最后一个安装的包。
  • 如果同一个包的两个版本提供不同的命名空间,您可以安装它们不同的虚拟环境,然后操作sys.path 以包含来自两者的包并同时获取两个命名空间。不过,我们的情况并非如此。
  • 另一种选择:重新打包一个版本的包,重命名命名空间。在这种情况下,更改软件包名称并将它们安装在同一个地方/venv 会更容易。您甚至可以将这个重新打包的包版本重新上传到 PyPI(我见过有人这样做),但由于依赖版本的不同,它不适用于 TF 和任何其他重要的包。有趣的事实:Python 包管理器不解析依赖版本;如果包 A 需要 C==1.0 而 B 需要 C==2.0,则在安装 A 和 B 后,您将只能获得一个版本的 C,从而削弱 A 或 B。

最后,为什么不应该这样做:这违反了 Python 原则。显式优于隐式,在运行时操作库路径并不是最透明和可支持的方法。尽管如此,维护一组兼容的依赖关系仍然存在问题,这就是我们有 venv、pipenv 等的原因。

从语言设计的角度来看,处理同一个包的多个版本确实有其好处,并被多种语言(例如 JS)采用。但是,它使进口商品变得更加昂贵。从历史上看,这是 Python 社区的一个小众场景,因此它不会超过性能损失。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-02
    • 1970-01-01
    • 2018-02-08
    • 1970-01-01
    • 2019-09-24
    • 2023-03-26
    • 1970-01-01
    相关资源
    最近更新 更多