📕 第2章 Python 的准备与基本语法
📄 一句话概括

pip 装包,本质上是从网上取一个 wheel 或源码包,解到当前环境的 site-packages, 再把依赖顺带装齐。 装不上只有四类原因:没网、包名错、缺编译环境、版本互相打架。 前三个都好查,第四个最难——所以要会看依赖树。 这一节的 Demo 把三件事一起查了:90 个已装包、numpy 2.5.2 与 matplotlib 3.11.1 满足要求、 tensorflow 与 keras 没装(本次环境装不了深度学习框架,正文全部改用 numpy 手写)。

📝 本节和前面几节的关系

上一节装的是解释器,这一节装的是包。 本节末尾那句「装不了 TensorFlow」很重要:第 3、4、5、6 章书里的代码都是 Keras 写的, 这台机器上没有,所以全部改成 numpy 手写等价实现,正文里会标清楚哪段是教材原码。 Anaconda 也在这一节讲,因为它本质上是「一次装几百个包」的另一种方案。

一、pip 装的到底是什么

pip install numpy 做的事:查 PyPI 上 numpy 的可用版本 → 挑一个和当前 Python 版本、 当前系统匹配的 wheel → 下载 → 解压到 Lib\site-packages → 把依赖也装上。 wheel 是预编译好的二进制包(.whl),装了就能用; 没有对应 wheel 时 pip 会下源码包(.tar.gz)当场编译,这时候才需要编译器。 Windows 上「装不上」十次有八次是后者:某个包没给 Windows 的 wheel, pip 想编译又找不到 Visual C++ 的编译工具,报一大堆红字。 先看 python -m pip install 包名 的最后三行,那里写着到底缺什么。 报「Microsoft Visual C++ 14.0 required」就是缺编译器; 报「Could not find a version」就是没网或包名错。

二、Anaconda 什么时候用

Anaconda 是一个打包好的发行版:Python 加几百个科学计算包(numpy、scipy、matplotlib、pandas、 scikit-learn、Jupyter……),一次装完。 好处是开箱即用,那些在 Windows 上编译麻烦的包,它给的是编好的二进制。 代价是体积大(几个 G),而且它有自己的包管理器 conda。 conda 和 pip 不要混着用在同一环境里。原因是 conda 只认自己装的包,pip 装的它不知道; 两者对依赖的解析逻辑也不同,混着装最容易被搞成一个谁也升不动的环境。 本书的做法:不装 Anaconda,需要什么 pip 装什么。这样环境里有什么一目了然。

三、版本约束与依赖树

numpy==2.5.2 是死锁,numpy>=2.0 是下限,numpy>=1.20,<3 是区间。 写死版本最稳,但也最容易和别人冲突。 依赖树长这样:matplotlib 要求 contourpy>=1.0.1、cycler>=0.10、fonttools>=4.28.2、 kiwisolver>=1.3.1……你只装了一个 matplotlib,结果多出十来个包,因为它们各自还要自己的依赖。 冲突的典型样子:A 要 numpy<2,B 要 numpy>=2,两个都要装, pip 会挑一个然后把另一个的约束降级警告打出来。 看到警告别忽略,那通常是后面莫名其妙的运行错误的源头。

四、四类失败与对应查法

报什么 真实原因 怎么查
Could not find a version 没网或包名错 pip -v;查 PyPI 的 wheel
Microsoft Visual C++ required 没 wheel,需要现场编译 换 Python 版本,或找预编译的 wheel
ERROR: pip's dependency resolver 版本冲突 pip install -v 看约束链
No module named xxx 装到了另一个环境 python -c "import sys; print(sys.executable)"

最后一行那种错最常见,也最好查: 先确认你 pip 用的解释器和跑代码用的解释器是同一个。两个不一致,装多少次都没用。 统一写法可以避免九成麻烦:python -m pip install xxx, 少用裸的 pip——pip 这个名字可能指向另一个环境的脚本。

五、动手 Demo:装了没有、够不够版本、为什么装不上

用 importlib.metadata 读当前环境的包清单和版本, 再用 packaging 把「numpy>=2.0」这种要求解析成可判断的对象,逐条核对。 最后故意 import 一个没有的包,看真实报错长什么样。

import importlib.metadata as md
from packaging.requirements import Requirement
from packaging.version import Version

for spec in ("numpy>=2.0", "matplotlib>=3.5", "tensorflow>=2.0"):
    r = Requirement(spec)
    try:
        cur = md.version(r.name)
        ok = r.specifier.contains(Version(cur), prereleases=True)
        print(spec, cur, ok)
    except md.PackageNotFoundError:
        print(spec, "没装")

print(md.requires("matplotlib"))      # 它自己要的那些包

实跑输出:

--------------------------------------------------------------------
函式库的安装~Windows 篇~:装了没有、够不够版本
--------------------------------------------------------------------

  这台机器上有 90 个已安装的包。按名字排序,前 8 个是:

  包名                  版本
  ----------------------------------
  Markdown              3.10.3
  PyPDF2                3.0.1
  PySDL2                0.9.17
  PyYAML                6.0.3
  Pygments              2.21.0
  anyio                 4.15.1
  av                    18.1.0
  bitarray              3.11.0

  装库最常遇到的三个问题,一个一个查:

  要求                  当前版本    结论
  ----------------------------------------------
  numpy>=2.0            2.5.2       满足
  matplotlib>=3.5       3.11.1      满足
  tensorflow>=2.0       没装        缺这个包
  keras>=2.0            没装        缺这个包

  **第一行第二行是「装了且够新」,第三行第四行是「压根没装」。**
  深度学习框架就卡在这两类问题上:要么没装,要么装的版本和别的库打架。

  真去装一个没有的包,报错长这样(这里是故意触发的):

  ModuleNotFoundError: No module named 'tensorflow'

  **看到这行别急着重装整个环境。**先问三件事:包名拼对了吗、
  这个包里有没有子模块(numpy.core 之类)、当前解释器是不是你以为的那个。
  第三件事最常见——开了两个环境,装的装到另一个去了。

  最后看一眼依赖长什么样,以 matplotlib 为例,它自己列了这些要求:

    contourpy>=1.0.1
    cycler>=0.10
    fonttools>=4.28.2
    kiwisolver>=1.3.1
  ……共 9 条。**你装一个库,pip 会顺着这张单子把它要的也装上**,
  所以「装一个」的结果常常是「多出五六个」——这就是依赖树。

90 个包,numpy 2.5.2、matplotlib 3.11.1 都满足要求;tensorflow 和 keras 直接是「没装」。 这台机器装不了深度学习框架,所以本书第 3 章之后,凡是书里用 Keras 写的,正文都标成「教材原码」, 实跑一律换成 numpy 手写。 ModuleNotFoundError: No module named 'tensorflow' 这一行是故意触发的。 注意它只说「没有这个名字的模块」,不说「包没装」——这两件事不一样:包可能装了, 但里面的子模块名不对,报错长得一模一样。 matplotlib 那张依赖单列了 9 条。pip 装它的时候,这 9 条会被顺着装下去。 所以「一个干净的 venv 装一个 matplotlib」结果会有十几个包——不是装错了,是依赖树本来就这么长。 这一段最实用的地方在于:判断「是不是环境问题」只要三步——打印 sys.executable、打印包版本、 import 一次看报错。三步之内能定位绝大多数问题。

30 秒实验

  1. 把 numpy>=2.0 改成 numpy>3.0,看结论那一列变成什么。
  2. 给 md.requires("matplotlib") 加个循环,把带 extra 标记(分号后面那段)的挑出来,看有几条。
  3. 在 venv 里跑同一段脚本,看包数从 90 变成几个——这就是「干净环境」的样子。

常见误区

  1. 用管理员权限 pip 装到全局。 装得快,出问题只能重装 Python。永远在 venv 里装。
  2. 看到红字就重装整个 Python。 先看最后三行:缺编译器就去装编译器或换版本,缺包就换个包名, 环境错了就切环境。重装是最贵的做法。
  3. 混用 conda 和 pip。 同一个环境里混着装,依赖解析各算各的, 最后往往整成一个谁也升不动的死环境。
自测三问(点开对答案) 1. wheel 和源码包有什么区别? —— wheel 是预编译的二进制包,解压就能用;源码包要现场编译,需要编译器和对应的库。 Windows 上装不上,多数是因为没有 wheel 而要现场编译。 2. 为什么推荐 `python -m pip install` 而不是直接 `pip install`? —— 因为 `python -m pip` 一定用当前这个解释器自带的 pip, 而裸 `pip` 可能指向 PATH 里另一个环境的脚本,装到别处去。 3. 依赖冲突的典型症状是什么? —— pip 打出 resolver 的警告或报错,说某个包要求 A 版本而另一个要求 B 版本; 或者装完之后运行时报「某个属性不存在」——那是版本降级导致的 API 变化。

Windows 的包装完了,Mac 上其实也一样,只是包管理器换成了 conda 或 brew。 下一节讲真正该学的一件事:怎么把环境写下来,换台机器一次装回去。

相关笔记