qinghuan
← 从零建立基础 / 全部笔记

单元 02 / 从零建立基础

P0 · 必会主线L0 · 零基础

建立能重复运行的 Python 练习环境

跑通第一个 Python 脚本,分清终端、解释器和项目环境,并记录别人怎样重建它。

本篇目录 · 按需要跳转

读完这篇,你能做什么

能在项目目录独立运行脚本,记录解释器路径,并在另一目录重建相同练习环境。

学习理由、准备要求与知识点

为什么学它

先确认脚本用的是哪套 Python、依赖装在哪里,才能分清代码错误与环境混用,也方便重新搭好相同实验。

先读这些内容

知识点清单

  • 终端、解释器与源文件
  • Python 版本检查
  • Windows py 启动器
  • 项目虚拟环境
  • 直接调用环境解释器
  • pip 与解释器绑定
  • UTF-8 源文件
  • 环境重建与凭证分离

运行前先确认Python内容以3.12官方文档为基线;Java17版本区间算法仅支持三段整数,不实现Python完整版本与依赖规范。

01先弄懂它是什么

学习 AI 时,Python 可以用来阅读模型示例和做小实验,Java 仍可以负责接口、存储和权限。第一步先让一个脚本稳定运行,并说清由哪个解释器执行、依赖装在哪、别人怎样重建环境。终端是输入命令和启动程序的入口,它当前所在的目录会影响相对路径,但终端本身不是 Python 解释器。

解释器负责读取并执行 Python 源文件。虚拟环境则为一个项目准备单独的解释器入口和第三方包目录,通常仍复用基础 Python 安装。比如甲项目升级依赖时,乙项目用自己的环境,就较不容易跟着导入失败。它主要分开项目依赖,不会隔离操作系统文件、网络或所有原生库,所以不等同于安全沙箱。

Windows上先用py -0p或当前启动器支持的列举命令确认解释器路径,再选定版本执行py -3.12 -m venv .venv。后续可直接运行.\.venv\Scripts\python.exe,不必把激活脚本是否成功当作环境是否存在的唯一依据。具体启动器能力随安装方式变化,验收时直接检查sys.executable最清楚。

文中这几个词,先认一认

解释器
真正读取和执行源文件的程序。它所在的路径和版本,决定本次代码由哪套 Python 运行。
虚拟环境
给项目单独放依赖和解释器入口的一组目录,减少不同项目互相影响;它不是容器。
依赖约束
项目允许使用哪些软件版本的规则。一个包要同时符合所有相关要求,不能只满足其中一条。

还有陌生词?到术语小抄里看一个具体例子 ↗

02看看它怎么工作

建立空目录,创建main.py并以UTF-8保存,内容可以只是输出一条中文消息和sys.executable。然后通过环境解释器运行这个文件,观察路径指向.venv且中文没有乱码。文件扩展名不能隐藏成main.py.txt;终端当前目录不对时,应使用明确文件路径,不要先反复安装Python。

安装包时使用.\.venv\Scripts\python.exe -m pip,把pip绑定到将要运行的解释器。单独输入pip可能命中另一个安装。遇到ModuleNotFoundError,先比较安装时与运行时的解释器路径,再检查包名和导入名;二者不总相同。只有确定环境一致后,才值得调查版本兼容和依赖缺失。

把版本范围看成集合交集有助于理解冲突:一个教程要求至少3.10,另一个依赖要求小于3.12,候选3.11满足二者,3.12不满足。算法只处理三个非负整数和左闭右开区间,展示判断过程;真实Python包版本还有预发布、平台标记与间接依赖,应交给符合规范的包管理器处理。

先跑通一个最小例子

先建一个空项目目录,把 main.py 以 UTF-8 保存到其中,再在该目录的 PowerShell 中逐行执行命令块;命令块无需另存或运行 .ps1。脚本使用 Python 3.12 兼容语法和标准库。

命令明确选择已安装的 3.12;若 py 不存在,可用 & 加带引号的已安装 Python 3.12 完整路径替代 py -3.12。版本选择失败时先解决解释器安装或路径问题,创建成功后再运行脚本。

main.py · python
import sys


def main() -> None:
    print("message=你好,Python")  # 固定消息用于检查文件编码和基本执行。
    version = sys.version_info
    print(f"python={version.major}.{version.minor}.{version.micro}")
    print(f"executable={sys.executable}")  # 核对真正启动本文件的解释器路径。
    print(f"in_venv={sys.prefix != sys.base_prefix}")  # 比较环境前缀,而非猜测终端提示符。


if __name__ == "__main__":
    main()  # 直接运行文件时执行;被导入时不自动打印。
PowerShell 命令 · powershell
# 在保存 main.py 的项目目录打开 PowerShell,逐行执行。
# 前提:Python 3.12 已安装,且 py -3.12 能选中它。
py -3.12 --version
py -3.12 -m venv .venv
# 创建成功后,直接使用环境解释器,无需运行 Activate.ps1。
.\.venv\Scripts\python.exe .\main.py

核对运行结果版本检查先输出 Python 3.12.x;脚本再输出四行: message=你好,Python python=3.12.x executable=<当前项目绝对路径>\.venv\Scripts\python.exe in_venv=True 其中 x 和完整路径随安装而变,不能逐字照抄示例路径。若改用其他兼容版本,版本行应与所选解释器一致。直接用系统解释器运行时 in_venv=False 是合理结果,只证明脚本运行成功;环境验收需另行核对 .venv 路径与 True。出现中文乱码则检查文件保存编码及终端显示设置。

正在加载流程图…

图中状态只描述本篇可执行机制;业务调用仍需由外层应用负责。
查看流程图源码(Mermaid)
flowchart TD
  P["选择解释器路径"] --> V["创建项目虚拟环境"]
  V --> F["保存 UTF-8 源文件"]
  F --> R["用环境解释器运行"]
  R --> E["核对 executable 与输出"]
  E --> D["记录依赖和重建命令"]

按顺序看,每一步做了什么

  1. 确认解释器的实际路径和版本,选一个项目基线。
  2. 创建项目目录和虚拟环境,保存UTF-8源文件。
  3. 用环境解释器直接运行文件并输出sys.executable。
  4. 通过同一解释器的-m pip安装需要的依赖。
  5. 记录依赖与重建步骤,在干净环境重复最小脚本。

03对照实现代码

OpenJDK 17 Runtime.Version 源码 (新标签页)↗

OpenJDK 17 GA,jdk-17+35 · Runtime.Version.compareTo

下方为原创 Java 教学片段,调用已核验的 JDK API 说明局部机制;不是该类原样源码,也不实现神经网络。完整程序见算法模块。

核心机制代码 · java
// Java 版本由数值段及可选预发布、构建信息组成,不能直接按字符串排序。
int order = Runtime.Version.parse("17.0.10")
        .compareTo(Runtime.Version.parse("17.0.9"));
boolean newer = order > 0; // 10 大于 9,结果为 true。
// 此 API 解析 Java 版本,不用于替代 Python 的版本规范。

Runtime.Version.compareTo委托内部比较逻辑,按版本组成部分比较,而不是对完整字符串做一次字典序比较。数字十和九的例子能够立即暴露字符串排序错误。

Java版本对象还包括预发布、构建及可选信息,比较规则比三个整数更丰富。本篇算法刻意固定小范围语法,便于证明区间交集;没有声称实现全部版本规范。

环境兼容不仅是大小比较。相同解释器版本在不同系统和处理器架构上可能需要不同二进制包;满足版本区间只能证明这一项约束通过。

真实源码值得阅读的部分是解析与比较分离:先把输入变成有约束的结构,再比较结构。这个思想也适用于模型配置、请求DTO与数据库迁移版本。

代码先调用谁,再调用谁

  1. 读取版本文本
  2. 解析为受约束的版本结构
  3. 按数值段比较
  4. 计算共同允许范围
  5. 选择实际已安装的最高可用版本

04动手写一个例子

解释器版本约束求交

每个版本是三个非负整数;每项约束为[lo,hi)。返回满足全部约束的最高已安装版本,没有则返回NONE。保存Main.java,用Java17编译运行固定自测。

输入 1
约束[3.10.0,3.12.0),候选3.9.0/3.11.2/3.12.0
输出 1
3.11.2
输入 2
两个区间无交集
输出 2
NONE

每增加一个区间,公共下界取较大者,公共上界取较小者。因为所有合法版本必须同时满足每个下界和上界,这两个极值已经完整表示当前共同允许的集合。

若最终下界不小于上界,交集为空。否则扫描实际候选,只保留落在区间内且比当前答案更大的版本,不需要先对所有候选排序。

上界不包含在区间中,所以3.12.0不能满足小于3.12.0。非法负数、缺少数字段或倒置区间要拒绝,不能把输入错误解释为正常的无可用版本。

为什么这样写是对的处理前i项约束后,[lo,hi)恰好等于这些区间的交集;扫描前j个候选后,best是其中满足全部约束的最大版本。

时间 O(r+n)额外空间 O(1)额外空间

r是约束数,n是候选数。版本固定三段,每次比较为常数;输入记录已在内存,算法只维护边界和当前答案。

保存为 Main.java,运行 javac --release 17 -encoding UTF-8 Main.java,再运行 java Main。程序包含固定输入自测,断言失败时会抛出错误。

展开收起代码 · Main.java · 完整可运行程序Java · 43 行

长行可在代码区域内左右滚动,按 Tab 可把焦点移入代码区。

Main.java · 完整可运行程序 · Java
import java.util.*;
public class Main {
    record V(int major,int minor,int patch) implements Comparable<V> {
        V { if (major<0 || minor<0 || patch<0) throw new IllegalArgumentException("negative version"); }
        public int compareTo(V b) {
            int c=Integer.compare(major,b.major); if(c!=0)return c;
            c=Integer.compare(minor,b.minor); return c!=0?c:Integer.compare(patch,b.patch);
        }
        public String toString(){return major+"."+minor+"."+patch;}
    }
    record Range(V lo,V hi) {
        Range { Objects.requireNonNull(lo);Objects.requireNonNull(hi);
            if(lo.compareTo(hi)>=0)throw new IllegalArgumentException("invalid interval"); }
    }
    static Optional<V> choose(List<Range> ranges,List<V> installed) {
        V lo=null,hi=null;
        for(Range r:ranges){
            // 左端取最大、右端取最小,保留全部约束的共同部分。
            if(lo==null||r.lo.compareTo(lo)>0)lo=r.lo;
            if(hi==null||r.hi.compareTo(hi)<0)hi=r.hi;
        }
        V best=null;
        for(V v:installed){
            Objects.requireNonNull(v);
            boolean ok=(lo==null||v.compareTo(lo)>=0)&&(hi==null||v.compareTo(hi)<0);
            if(ok&&(best==null||v.compareTo(best)>0))best=v;
        }
        return Optional.ofNullable(best);
    }
    static void check(String expected,Optional<V> got){
        if(!expected.equals(got.map(V::toString).orElse("NONE")))throw new AssertionError(got);
    }
    public static void main(String[] args){
        V a=new V(3,10,0),b=new V(3,12,0),c=new V(3,11,2);
        check("3.11.2",choose(List.of(new Range(a,b)),List.of(a,b,c)));
        check("NONE",choose(List.of(new Range(a,b)),List.of(b)));// 不包含上界。
        check("NONE",choose(List.of(new Range(a,c),new Range(c,b)),List.of(c)));
        check("NONE",choose(List.of(),List.of()));
        check("3.12.0",choose(List.of(),List.of(c,b)));// 无约束选最大。
        try{new Range(b,a);throw new AssertionError();}catch(IllegalArgumentException expected){}
        System.out.println("PASS 6 tests");
    }
}

这些特殊情况也要试一试

  • 候选为空时返回NONE,不生成一个并未安装的版本。
  • 相邻区间[甲,乙)与[乙,丙)没有交集。
  • 比较整数时使用Integer.compare,避免减法溢出。
进阶选读 · 后续再读:依赖图约束求解

场景多个包具有可选版本及彼此依赖,选择某个版本会改变其他包的允许范围。

做法把包版本选择建模为约束满足问题,利用传播和回溯搜索;读取锁文件并按平台重建。区间求交只是约束传播的局部步骤。

复杂度一般依赖选择可包含组合搜索,不能用本题线性复杂度承诺全部解决;最坏情况需要指数级探索。

好处与代价锁定版本增强复现性,但仍需更新安全修复并验证目标平台,不能永久冻结后不再维护。

05项目里怎么用

环境记录至少包含解释器版本、依赖声明、启动命令和必要配置键的名称。凭证值应由环境变量或受控凭证管理提供,示例文件只给占位符。虚拟环境目录通常可以重建,不宜把整份环境复制当成跨机器可移植交付;其中的路径和原生扩展可能绑定原安装。

发生故障时按层排查:找不到命令看路径,找不到文件看工作目录,语法错误看源代码位置,导入失败看解释器和依赖,网络失败再看连接配置。这种顺序能减少“所有问题都重新安装”的无效操作。最小脚本只证明本地执行通路,不证明模型服务、认证和远端数据可用。

Java 后端学习者可以把 Python 实验放进单独目录,保存一份输入 JSON 和对应输出样本,再由 Java 接入层实现相同的输入输出约定。先让两个进程怎样交换数据足够清楚,避免后端服务暗中依赖某个 Python 临时变量、全局安装的包,或只在开发电脑上存在的绝对路径。

容易踩的坑

  • 激活提示出现环境名就认定所有命令均来自该环境。
  • 使用全局pip安装,再用另一解释器运行。
  • 把版本字符串字典序当成数值版本顺序。
  • 把复制.venv目录当成跨系统可复现部署。

几种做法,怎么选

表格有四列,窄屏可左右滑动查看代价和适用条件。

方案优势代价与限制适用条件
直接环境解释器路径调用目标明确,适合脚本与排障命令较长,移动目录需调整入门与自动化运行
激活虚拟环境交互命令简短依赖当前终端状态本地连续开发
容器化运行可进一步固定系统依赖增加构建与镜像管理成本涉及原生库或一致部署要求

06检验是否读懂了

先自己想、自己写,再展开答案核对。场景题是假设的练习情境;遇到还没学过的内容,可以做完基础后再回来。

先用自己的话解释

01虚拟环境隔离什么?

展开参考答案与推导

主要隔离项目的包安装位置与解释器入口;不自动限制文件、网络、进程权限,也不保证跨操作系统二进制兼容。

02为什么使用python -m pip?

展开参考答案与推导

让当前解释器加载pip模块,减少pip与运行解释器来自不同安装的歧义;仍应先核对解释器路径。

03为什么区间上界取最小值?

展开参考答案与推导

满足所有“小于某版本”要求的候选必须小于最严格的那个上界;更大的上界不会放宽另一个依赖的限制。

换个情况,还会处理吗

01同事能运行,你导入失败,先查什么?

展开参考答案与推导

比较sys.executable、版本、依赖声明和工作目录;确认运行目标一致后再看包名、平台和安装日志。

02没有权限执行激活脚本,还能使用环境吗?

展开参考答案与推导

可以直接调用环境内解释器;激活主要调整终端环境变量,环境目录存在与能否执行激活脚本是不同问题。

03所有候选满足版本范围但安装失败,是否算法错误?

展开参考答案与推导

不一定。范围比较只验证解释器版本;包平台支持、原生依赖、下载源和冲突的间接依赖仍需检查。

写代码试一试

01补一个下界测试

输入 / 条件
范围[3.10.0,3.12.0),候选3.10.0
输出 / 预期
3.10.0

提示 左端闭合。

展开参考答案与推导

比较条件必须是大于等于下界;若误写大于,会错误排除最低受支持版本。

02两个兼容范围怎样合并?

输入 / 条件
[3.9.0,3.13.0)与[3.11.0,3.12.0)
输出 / 预期
[3.11.0,3.12.0)

提示 分别处理左右边界。

展开参考答案与推导

最大下界为3.11.0,最小上界为3.12.0;仍需扫描实际已安装候选,不能假设区间里的每个版本均存在。

03为什么不需要排序候选?

输入 / 条件
3.11.1,3.10.9,3.11.8
输出 / 预期
最高兼容者3.11.8

提示 维护迄今最大值。

展开参考答案与推导

每次只比较当前候选和best,一遍即可得到最大值;排序会增加O(n log n)工作且对本题没有收益。

读完之后,独立完成

课后作业:自己做一遍

本篇共 10 道作业,先完成 8 道基础与综合任务,再按前置知识挑战进阶题。其中包含 1 道思维拓展,试着换个条件,看看原来的办法是否还管用。不附参考答案。

这次练什么

能在项目目录独立运行脚本,记录解释器路径,并在另一目录重建相同练习环境。

本篇重点加练

虚拟环境和包管理器的对应关系影响复现,重点检查环境重建与依赖归属。

  • 项目虚拟环境
  • pip 与解释器绑定

重点内容会多练几次。先说清楚怎么做,再试试空数据、出错或条件改变时会发生什么。

先自己做,卡住时记下问题,再回来查。问答题写清“为什么、什么时候适用”;动手题留下输入、输出和运行步骤,方便下次重做。

基础必做

  1. 作业 01原理问答

    画出终端、解释器、源文件与当前目录的关系,并在你的电脑记录实际解释器版本和路径,解释版本输出可以证明什么。

    提交内容
    一份独立书面回答或标注图,包含自造例子、概念边界及判断依据。
    怎样算完成
    • 逐项回应题目要求,区分事实、假设与个人判断。
    • 能用自己的例子解释适用条件,记录尚未弄懂的点。

    练到的知识点终端、解释器与源文件 · Python 版本检查

  2. 作业 02代码推演

    在Windows写出使用py启动器创建两个项目环境的操作记录,逐项标出环境目录与源文件目录;若用其他系统,记录对应替代命令。

    提交内容
    手工推演记录,写明输入约定、中间状态、最终判断;可运行时附程序对照记录。
    怎样算完成
    • 推演包含中间过程,数据形状、单位、顺序或状态前后一致。
    • 主动检查至少一个边界或异常条件,能解释手算与运行差异。

    练到的知识点Windows py 启动器 · 项目虚拟环境

  3. 作业 03原理问答

    给项目目录、虚拟环境目录、解释器与pip各画一条“属于谁”的关系线;说明项目改名与环境重建可能分别影响哪些引用。

    提交内容
    概念或合同说明、关系图与自造案例,明确事实、假设和适用条件。
    怎样算完成
    • 关键概念有明确边界,能用案例检验声明。
    • 未获证据支持的假设单独标注。

    练到的知识点项目虚拟环境 · pip 与解释器绑定

综合应用

  1. 作业 04面试场景

    “编辑器能导入模块,终端却报ModuleNotFoundError”。设计检查顺序,记录运行解释器与安装包解释器分别来自哪里。

    提交内容
    场景分析与决策记录,包含信息缺口、约束、备选方案和验证计划。
    怎样算完成
    • 方案回应给定约束,列出至少一种替代方案及代价。
    • 每项关键结论说明所需证据,不能核实的部分明确标注。

    练到的知识点直接调用环境解释器 · pip 与解释器绑定

  2. 作业 05编程实践

    创建含中文文本的最小Python源文件,输出学习主题与实际解释器路径;凭证仅用占位值并单独记录配置要求。在第二个新建虚拟环境中重建依赖并运行,提交命令、版本与输出记录。

    提交内容
    可复现的最小程序或实验材料、自写测试、环境说明与运行记录;题目允许纸面实验时提交完整案例表。
    怎样算完成
    • 提交物按说明可以重现;测试覆盖正常、边界与失败场景。
    • 运行证据与断言能核验声明的行为,注明模拟与真实执行范围。

    练到的知识点UTF-8 源文件 · 环境重建与凭证分离

  3. 作业 06排错实验

    刻意从不同工作目录运行同一脚本,并改变文件编码,记录出现的错误和路径解析依据;不要删除系统Python来修复环境。

    提交内容
    最小故障输入、复现步骤、观测记录、定位依据及回归用例。
    怎样算完成
    • 其他人可按记录复现故障,并区分现象与已验证原因。
    • 定位过程有观测依据,回归用例可以再次暴露原问题。

    练到的知识点终端、解释器与源文件 · 直接调用环境解释器 · UTF-8 源文件

  4. 作业 07编程实践

    在专用练习目录创建临时环境,不安装第三方包,用该环境解释器查询pip的版本与路径;重建同名临时环境后再次记录,比较环境身份与源码文件是否属于同一生命周期。

    提交内容
    可复现的小程序或题目允许的离线实验、自写测试、输入和运行记录。
    怎样算完成
    • 输入、环境和运行过程可重现,测试覆盖声明的重点条件。
    • 验证目标由合同确定,模拟结果不冒充真实服务证据。

    练到的知识点项目虚拟环境 · pip 与解释器绑定

  5. 作业 08排错实验

    将含空格的项目路径写入一份新终端运行说明,用已有最小中文脚本验证每条命令;检查激活状态与直接指定解释器两种入口是否指向声明的环境。

    提交内容
    最小故障序列、观测记录、原因核对与可再次触发原问题的测试。
    怎样算完成
    • 故障可以稳定重现,现象与已验证原因分开。
    • 观测记录足以判断声明的合同是否被违反。

    练到的知识点项目虚拟环境 · pip 与解释器绑定

进阶挑战 · 掌握前置后选做

  1. 作业 09设计评审

    在掌握命令行后,给同学编写环境复现说明,要求不依赖你机器的绝对路径;用一个干净目录实际验证说明。

    提交内容
    设计评审文档或对照实验报告,包含前置条件、方案取舍、验收证据与尚未验证的范围。
    怎样算完成
    • 比较使用一致条件与事先声明的标准,记录失败与未知结果。
    • 说明方案的适用前提、未覆盖风险及后续验证方法。

    练到的知识点Python 版本检查 · 项目虚拟环境 · 环境重建与凭证分离

思维拓展

先写出你的猜想,再用例子或实验验证。答案可以不同,但要说清依据、适用情况,以及还有什么没弄懂。

  1. 作业 10思维拓展 · 反例构造

    为“源码和requirements文件相同,任何两台电脑都必定获得相同运行环境”列出一个具体反例条件,并设计能够发现该差异的最小环境报告。

    提交内容
    具体反例、原主张的适用前提、复现或逻辑证明及结论边界。
    怎样算完成
    • 反例满足原主张所声称的前提,确实针对其结论。
    • 说明一个反例能推翻什么、不能进一步证明什么。

    练到的知识点项目虚拟环境 · pip 与解释器绑定

完成后,再检验一次

给每题标记“独立完成 / 查阅后完成 / 尚未完成”。查过资料或没做出的题,记下卡在哪里。隔一天换一组输入再试,能解释结果和出错原因,再把这个知识点标为掌握。

继续阅读

先看能帮你完成当前练习的部分。例子跑通后,再读更深入的解释。

打开资料库,找下一份学习材料 ↗