第14章 计算机系统的本质

学习目标

  • 理解科研开发中常见系统术语的真实含义
  • 分清 OS、语言、包、环境、IDE、终端、脚本的层级关系
  • 能用统一框架解释“为什么代码在不同机器表现不同”

关键概念

  • 操作系统 (OS)
  • 语言 / 引擎 / 内核
  • 包 (Package/Library)
  • 环境 (Environment)
  • Docker
  • 镜像源 (Mirror)
  • IDE 与终端 (Terminal)
  • 脚本 (Script)

正文

用“做饭”理解计算机系统

可以把开发过程想象成做饭:你想做出一顿完整的饭,但你会遇到“房子、厨房、厨具、料理包、菜谱、超市”等不同层次。

这个类比对应到计算机系统如下。

术语 类比 例子
操作系统 OS 房子。可以在房子里再套房子(虚拟机/WSL),但底层资源总量有限。 Windows、Linux、macOS
语言 / 引擎 / 内核 厨房风格。你可以在不同风格厨房做饭。 MATLAB、R、Python
包 / 库 料理包。很多功能不用自己从零写。 FieldTrip、ggplot2、PyMC
环境 房子里的某个具体厨房,包含语言版本和全部包。 R 4.4 环境、Python 3.11 环境
Docker 按标准模板打包好的厨房。拎包入住。 datascience notebook、hDDM 容器
镜像源 购买料理包的超市。 CRAN 清华源、conda-forge 镜像
IDE 你常用的一套厨具。 VS Code、RStudio、PyCharm
终端 Terminal 极简火坑。可直接下命令,不靠图形界面。 PowerShell、bash、cmd
脚本 Script 菜谱。规定处理步骤。 .py.R.m

让我们用几个具体的例子来加深理解:

  • PsychoPy是一个“基于Python”的实验设计和呈现平台。它的运行需不需要首先有一个Python?

    需要。“基于Python”意味着它是用Python写的,它的代码也必须由Python来执行,所以必须先有一个Python,才能运行PsychoPy。具体而言,“一个Python”的正式说法是“Python解释器”,在Windows系统上,它是一个叫python.exe的文件。

  • 你可以在PsychoPy的官网下载到“Stand-alone”版本的安装包。安装前后,需不需要安装Python?

    不需要。“Stand-alone”版本意味着安装好之后,它就是一个独立的、打包好的、即开即用的软件包;你会在安装目录中找到一个Python(python.exe)。当你打开PsychoPy时,实际上是这个Python在充当专属于PsychoPy的解释器;即使你已经在系统上安装过另一个Python,也与PsychoPy无关。

  • 安装好PsychoPy、采集数据之后,你准备用numpypandas甚至hddm等Python包来分析数据。既然刚才安装好的PsychoPy自带一个Python解释器,你需不需要再安装一个新的Python?

    需要,除非你在安装PsychoPy之前安装过另一个Python。如前所述,虽然PsychoPy自带一个Python,但那个Python是专属于PsychoPy的。你需要另一个Python(不管是之前安装过的,还是再安装一个)来完成你的数据分析任务。最好的方案是,对于每一个项目,都使用一个独立的Python(甚至是R),就像PsychoPy拥有一个自己的Python一样。如何创建、管理这些不同的Python,也就是环境管理的意义。

  • 也许你的IDE可以识别到PsychoPy目录下的那个Python——如果就是想用它来做数据分析之类的工作,会有什么后果?

    最好的情况下,不会有什么严重后果。但是,也有可能因为“依赖问题”导致你的PsychoPy环境受到污染,最终无法正常运行。保险起见,除非你清楚自己在做什么,否则不要跨项目、跨环境使用Python。

  • 到底什么叫“依赖问题”?

    可以把“依赖”理解为:你的代码要跑起来,不只需要你写的那几行代码,还需要一整套“外部条件”一起配合。最常见的依赖包括:Python/R 版本、第三方包版本、系统库、甚至操作系统本身。只要其中某一项版本不匹配,代码就可能报错。这就叫“依赖问题”。

    想象你要做小组汇报,准备了PPT和相应的逐字稿,然后你把逐字稿发给了演讲人。但是,你又修改了PPT的内容,有很多新增和删除页面。此时,你的逐字稿就出现了“依赖问题”:PPT上的第7页不再是你逐字稿的第7页。或者说,你和演讲人不是同一个环境(PPT的版本不一致),导致演讲人没法正确进行汇报。


依赖问题:到底在“依赖”什么?

很多初学者以为:

我已经安装了 Python,为什么还会 ModuleNotFoundError

核心原因是:你运行的不是“一个Python世界”,而是“某个具体环境里的某个Python”。

依赖问题通常发生在下面三层:

  1. 语言依赖:代码要求 Python 3.11,但你在 3.8 里运行。
  2. 包依赖:你的代码用到 pandasnumpypymc,但当前环境没装,或版本不对。
  3. 系统依赖:某些包需要系统级组件(编译器、动态链接库、CUDA 驱动等)。

一句话总结:

代码不是孤立运行的,它依赖一整套上下文。

什么是“依赖冲突”?

依赖冲突就是:

  • 包A要求 numpy < 2.0
  • 包B要求 numpy >= 2.0

结果:你无法在同一个环境里同时满足二者。

这和“一个厨房里两位厨师对同一种调料有互斥要求”非常像:

  • 厨房里只能有一种酱油
  • 厨师A说必须用低盐酱油
  • 厨师B说必须用高盐酱油

你只能二选一,或者拆成两个厨房(两个环境)。

环境是什么?为什么它能解决依赖问题?

环境(Environment)可以理解成“项目专属厨房”:

  • 有自己的语言版本
  • 有自己安装的包
  • 与其他项目隔离

这样做的好处:

  1. 项目A升级包,不会弄坏项目B。
  2. 旧项目可以长期维持可复现状态。
  3. 出问题时更容易定位(只在一个环境里查)。

最实用的经验:

一个项目一个环境,不要多个项目共用一个 base

依赖问题的典型报错长什么样?

  1. ModuleNotFoundError: No module named 'xxx'
    通常是:当前环境没装这个包,或 IDE 选错解释器。

  2. ImportError / DLL load failed
    通常是:包安装不完整,或系统依赖缺失。

  3. TypeError: got an unexpected keyword argument ...
    通常是:包版本变化,函数接口变了。

  4. 同一份代码在A电脑能跑,在B电脑报错
    通常是:环境不一致(语言版本、包版本、系统差异)。

三步排查法(新手版)

第一步:确认“我现在在哪个环境”

  • 看 IDE 右下角解释器
  • 在终端打印 Python/R 版本
  • 确认你装包的环境和你运行代码的环境是同一个

第二步:确认“我缺什么依赖”

  • 看报错里缺的是哪个包/哪个版本
  • 用包管理器查询是否已安装
  • 必要时在干净环境里最小复现

第三步:固定并记录版本

  • Python:requirements.txtenvironment.yml
  • R:renv.lock
  • MATLAB:记录 toolbox 与版本

这一步决定你半年后还能不能复现今天的结果。

一个最小示例:为什么会冲突

假设你有两个项目:

  • 项目A(旧):依赖 pandas==1.5
  • 项目B(新):依赖 pandas==2.2

如果放在同一个环境里,升级到 2.2 后,项目A 可能出现接口不兼容。

正确做法:

  1. env_A 固定旧版本,保证历史项目可复现。
  2. env_B 使用新版本,支持新功能开发。

这就是“环境隔离”最直接的价值。

你真正需要记住的三句话

  1. 环境是项目边界,不是可有可无的配置。
  2. 依赖冲突很正常,不是你水平差。
  3. 能复现的关键不是记忆,而是版本记录。

注意事项与常见问题

同一段代码在不同机器可能有不一样的结果

常见原因基本都来自“层级错配”:

  1. 操作系统不同:路径分隔符、权限系统、系统依赖不一致。
  2. 语言版本不同:同一个包在 Python 3.9 和 3.11 行为可能不同。
  3. 包版本不同:函数参数、默认行为、返回结果可能变化。
  4. 环境不一致:你在 envA 装包,实际运行却在 base
  5. 镜像源/网络差异:安装失败或装到旧版本。

结果复现不是简单的复制代码

拥有原始代码文件,不代表你就能复现运算结果。要完全复现,必须在以下四个方面都保持一致:

  1. 数据层:原始数据与预处理规则一致。
  2. 脚本层:分析脚本、参数设置一致。
  3. 环境层:语言与包版本一致。
  4. 执行层:运行入口、路径、权限一致。

任何一个层面出现不一致,都可能导致复现失败,甚至代码不可运行。

必须掌握的三个实践原则

原则1:明确“当前在哪个环境”

  • 不要默认自己在正确环境中。
  • 每次运行前确认解释器路径与环境名。
  • IDE 右下角解释器信息必须看一眼。

原则2:区分“代码错误”和“环境错误”

  • SyntaxErrorNameError 通常是代码逻辑问题。
  • ModuleNotFoundErrorpackage not available 通常是环境问题。
  • 路径找不到通常是工作目录问题。

原则3:固定版本,减少漂移

  • 在项目中记录版本信息(如 requirements.txtenvironment.yml)。
  • 包升级前先做小范围测试。
  • 能稳定跑通就不要频繁更换主环境。

小结

  • 计算机系统是分层结构,不是“一个软件包打天下”。
  • 大多数“玄学报错”都可以在系统层级中定位。
  • 把 OS、环境、包、脚本关系理顺,开发效率会明显提升。

练习与思考

  1. 用“做饭类比”解释给同学听:环境IDE 的区别是什么?
  2. 记录一次你遇到的报错,判断它更可能属于哪个层级(OS/包/环境/脚本)。
  3. 写出你当前项目的“运行依赖清单”:OS、语言版本、关键包、IDE、运行入口。