本讲义的核心目标是在受控的Linux环境中学习并自主使用Python,涵盖文件树结构、权限管理、Bash shell、基本命令、环境变量、Python虚拟环境、git以及Python工具链等内容。
文件系统
Linux文件系统的核心理念
Linux操作系统设计理念:一切皆文件。这意味着不仅普通的文档是文件,程序、目录、外部设备(如U盘、打印机)、进程间通信的管道、系统日志等都被抽象为文件的形式来管理。这种统一的抽象方式使得系统管理变得一致且简洁。
整个文件系统从一个唯一的根目录 / 开始,向下延伸形成树状结构。与Windows系统中每个磁盘有独立盘符(如C盘、D盘)不同,Linux将所有存储设备都挂载到这棵统一的文件树中。
几个标准目录需要了解:
/bin存放系统启动和运行所必需的基本程序,如ls、cp等核心命令/usr/bin存放当前系统安装的应用程序/home/<user>是普通用户的个人主目录,存放用户自己的文件和配置/etc存放系统级的配置文件,如网络配置、用户账户信息等
目录导航命令
pwd 命令(print working directory)用于显示当前所在的工作目录。执行 !pwd 后,系统会返回当前路径,例如 /root 或 /home/username。
几乎所有Linux命令都有对应的手册页面,通过 man 命令(manual的缩写)可以查阅。例如 man pwd 会显示pwd命令的详细说明,包括用法、选项和示例。如果系统没有安装man,可以使用 --help 选项作为替代,执行 !pwd --help 同样能获取帮助信息。man 命令本身也有手册页,可以通过 man man 查阅。
ls 命令(list)用于列出目录中的文件。执行 !ls $HOME 会列出个人主目录中的所有文件和文件夹。这里 $HOME 是一个环境变量,指向当前用户的主目录。
cd 命令(change directory)用于切换目录。cd / 进入根目录,cd .. 返回上一级目录,cd - 返回上一次所在的目录,cd $HOME 或直接 cd 返回主目录。以下是一个连续操作的示例:
echo "进入根目录 /"
cd /
pwd
echo "进入 /usr"
cd /usr
pwd
echo "返回 HOME"
cd $HOME
pwd
上述代码执行后依次输出 /、/usr、以及主目录路径。
执行 !ls /bin 可以查看 /bin 目录中的完整文件列表,这里存放着系统最基本的可执行程序。
which 命令用于定位可执行程序的实际路径。执行 !which ls 会返回 /usr/bin/ls 的结果,即路径
权限和权利
权限的基本概念
Linux是一个多用户操作系统,权限管理是其安全机制的核心。每个文件都具有三个属性:一个所有者(创建该文件的用户)、一个所属组(用户可以属于多个组)、以及三组权限设置。
权限分为三种类型:
- 读取权限(r,read)允许查看文件内容或列出目录内容
- 写入权限(w,write)允许修改文件内容或在目录中创建删除文件
- 执行权限(x,execute)允许运行程序文件或进入目录
对于可执行的二进制文件或脚本,必须具有执行权限才能作为程序运行。通过 ls -l 命令可以查看文件的详细信息,输出格式如下:
-rwxr-x--- 1 alice users 1200 test.sh
其中 -rwxr-x--- 分解为:
- 第1位表示文件类型,
-代表普通文件,d代表目录,l代表符号链接 - 第2到4位是所有者的权限,此处
rwx表示可读、可写、可执行 - 第5到7位是所属组的权限,
r-x表示可读、不可写、可执行 - 第8到10位是其他所有人的权限,
---表示没有任何权限
权限操作实践
touch 命令用于创建空文件或更新文件的时间戳。执行 !touch myfile.txt 创建一个名为myfile.txt的空文件。
执行 !ls -l myfile.txt 查看其权限,输出类似:
-rw-r--r-- 1 root root 0 ... myfile.txt
对照权限位的含义:第1位 - 表示普通文件;第2-4位 rw- 表示所有者(root)可读、可写、不可执行;第5-7位 r-- 表示组(root)可读、不可写、不可执行;第8-10位 r-- 表示其他人可读、不可写、不可执行。
chmod 命令(change mode)用于修改文件权限。执行 !chmod u+x myfile.txt 为用户添加执行权限。这条命令的各部分含义为:chmod 是修改权限的命令,u 代表user即文件所有者,+ 表示添加权限,x 表示执行权限,myfile.txt 是目标文件。
常用的符号包括:
u代表所有者,g代表组,o代表其他人,a代表所有人+添加权限,-移除权限r读权限,w写权限,x执行权限
执行 !chmod g+w myfile.txt 给组添加写入权限,然后用 !ls -l myfile.txt 验证更改是否生效。
八进制表示法
权限除了用符号表示外,还可以用八进制数字表示。八进制值的计算公式为:
每种单独的权限对应一个数值:
将需要的权限值相加即可得到一组权限的数值。例如:
命令 chmod 754 fichier 将文件权限设置为:所有者(user)得到7即rwx,组(group)得到5即r-x,其他人(other)得到4即r--。
常见的权限组合有 600(所有者可读写,其他人无权限,适合私密文件)和 644(所有者可读写,其他人只读,适合公开但不允许修改的文件)。
八进制权限实践
执行 !touch testfile.txt 创建测试文件。要设置 rw-r----- 权限,计算八进制值:所有者 rw- 为 4+2+0=6,组 r-- 为 4+0+0=4,其他人 --- 为 0+0+0=0,因此执行 !chmod 640 testfile.txt,再用 !ls -l testfile.txt 验证。
对于 rwxrwxr-x 权限,所有者 rwx 为7,组 rwx 为7,其他人 r-x 为5,执行 !chmod 775 testfile.txt 后用 !ls -l testfile.txt 验证。
两个极端情况:
000对应----------,表示没有任何人有任何权限,文件完全锁定777对应-rwxrwxrwx,表示所有人都有读、写、执行权限,文件完全开放(通常不建议使用,存在安全风险)
其他基本命令
常用命令概览
Shell允许通过简单的程序与系统交互
- 文件查看类命令包括
cat(显示全部内容)、less(分页查看)、head(显示开头)、tail(显示结尾) - 文本搜索使用
grep,文件搜索使用find - 文件管理命令包括
mkdir(创建目录)、cp(复制)、mv(移动或重命名)、rm(删除)
命令实践
执行 !mkdir -p $HOME/tp1 在主目录下创建名为tp1的文件夹。-p 选项的作用是:如果父目录不存在则一并创建,如果目录已存在也不报错。在Colab环境中,$HOME 指向 /root 目录。
执行 !cp /etc/hostname $HOME/tp1/ 将系统的hostname文件复制到tp1文件夹中。/etc/hostname 是存储主机名的系统文件。
执行 !head $HOME/tp1/hostname 显示文件的前10行。head 命令默认显示前10行,可以用 -n 选项指定行数,如 head -n 5 显示前5行。
执行 !grep -r "root" $HOME/tp1/ 在tp1目录中搜索包含关键词root的行。-r 选项表示递归搜索,会遍历目录下的所有文件。
执行 !find $HOME -name "*.py" 在主目录下搜索所有扩展名为.py的Python文件。find 命令的基本结构是 find 搜索路径 -name "匹配模式",* 是通配符,匹配任意字符。
环境变量
环境变量的作用
环境变量是操作系统中用于存储配置信息的一种机制,它们定义了系统和程序的运行行为。当一个程序启动时,它会继承当前shell的环境变量,从而知道去哪里找文件、使用什么配置等。
几个核心环境变量需要掌握:
PATH是最重要的环境变量之一,它包含一系列目录路径,用冒号分隔,当你在终端输入一个命令时,系统会按顺序在这些目录中查找对应的可执行文件HOME指向当前用户的个人主目录,是很多程序存放用户配置文件的默认位置SHELL记录当前使用的shell程序路径,如/bin/bash或/bin/zshPYTHONPATH是Python特有的环境变量,指定Python解释器在导入模块时额外搜索的目录
访问环境变量的值需要在变量名前加 $ 符号,使用 echo 命令可以将其打印出来。
环境变量操作实践
执行以下命令显示几个常用变量:
!echo $HOME
!echo $PATH
!echo $SHELL
输出结果如下:
/root
/opt/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/tools/node/bin:/tools/google-cloud-sdk/bin
/bin/bash
$HOME 会输出类似 /root 或 /home/username 的路径,$PATH 会输出一长串用冒号分隔的目录路径,$SHELL 会输出当前shell的路径。
使用 export 命令可以临时创建或修改环境变量:
%%bash
export TP_VAR="bonjour"
这条命令创建了一个名为 TP_VAR 的变量,值为字符串 bonjour。执行 !echo $TP_VAR 可以验证变量是否存在。
环境变量的一个关键特性是它只存在于当前shell及其子进程的环境上下文中。如果你启动一个新的独立shell会话,之前定义的变量将不存在。再次执行 !echo $TP_VAR 会发现变量为空,因为每个 ! 命令在Jupyter/Colab中都会启动一个新的shell进程。
向 PATH 添加新虚拟目录的方法是:
%%bash
export PATH="$PATH:$HOME/monfakebin"
这条命令将 $HOME/monfakebin 追加到现有的 PATH 末尾。语法 $PATH:新路径 的含义是保留原有的PATH内容,然后用冒号连接新的目录。执行 !echo $PATH 可以验证新路径是否已添加。
一旦某个目录被加入 PATH,该目录中的可执行程序就可以直接通过文件名调用,无需输入完整的绝对路径或相对路径。
文件 $HOME/.profile(波浪号 ~ 是 $HOME 的简写形式)会在每次用户登录会话启动时自动被读取执行。将 export 语句写入这个文件,可以使环境变量的设置永久生效,不必每次手动输入。
Shell编程能力
Shell不仅能运行程序和操作文件,还具备完整的编程能力,包括条件判断、循环结构、函数定义等。执行 ls -l /usr/bin 可以看到系统中安装了数量众多的可执行程序。Shell编程是一个独立的大话题,本课程不做深入讨论。
使用uv的Python项目
uv工具简介
uv 是一个现代化的Python环境管理工具,使用Rust语言实现,因此执行速度极快。它的设计目标是替代Python生态中几个传统工具的功能:pip(包安装器)和 virtualenv(虚拟环境创建工具)。
uv的核心功能包括:
- 创建与系统Python隔离的独立环境,避免不同项目之间的依赖冲突
- 以极快的速度安装Python包
- 直接执行带有依赖声明的脚本
- 通过标准的
pyproject.toml配置文件管理完整的Python项目
Ubuntu下安装uv
安装uv的命令为:
curl -LsSf https://astral.sh/uv/install.sh | sh
这条命令的工作原理是:curl 是一个从URL下载内容的工具,-LsSf 是一组选项(L表示跟随重定向,s表示静默模式,S表示显示错误,f表示失败时不输出内容)。下载的内容是一个shell脚本(.sh扩展名)。管道符 | 将下载的内容传递给 sh(通用shell解释器)执行。这种从网络直接下载并执行脚本的方式存在安全风险,因为你无法事先审查脚本内容,应只对可信来源使用。
安装完成后,需要确保uv的二进制文件所在目录在 PATH 中:
export PATH="$HOME/.local/bin:$PATH"
执行 uv --version 验证安装是否成功。在Colab环境中的完整安装过程:
!curl -LsSf https://astral.sh/uv/install.sh | sh
!export PATH="$HOME/.local/bin:$PATH"
!uv --version
uv常用命令
uv init 用于在当前目录初始化一个新的Python项目,会创建 pyproject.toml 配置文件。uv venv 用于创建和管理虚拟环境。uv add 用于在当前环境中安装Python包,并自动更新项目配置文件。uv run 用于在项目的虚拟环境中执行脚本或命令。使用 uv help <cmd> 可以查看任意命令的详细文档。
项目实践
首先创建项目目录并初始化:
!mkdir -p /content/myproject
%cd /content/myproject
!uv init
mkdir -p 创建目录,%cd 是Jupyter的魔法命令用于切换当前工作目录,uv init 初始化项目。
接下来创建一个使用 numpy 和 matplotlib 绘制正弦曲线的脚本 main.py:
%%writefile main.py
#!/usr/bin/env python3
import matplotlib
matplotlib.use('Agg')
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(x)
plt.plot(x, y)
plt.title("Sine Curve")
plt.xlabel("x")
plt.ylabel("sin(x)")
plt.savefig("sine.png")
plt.show()
%%writefile main.py 是Jupyter魔法命令,将单元格内容写入文件。matplotlib.use('Agg') 设置非交互式后端,适合在没有图形界面的环境中保存图片,这里是因为用的colab。
尝试执行脚本:
!uv run python main.py
此时会报错,因为虚拟环境中尚未安装 numpy 和 matplotlib。
使用uv安装所需的包:
!uv add numpy matplotlib
uv add 会自动下载包、解决依赖关系、安装到虚拟环境,并更新项目配置文件。
检查 pyproject.toml 的变化:
!cat pyproject.toml
结果如下:
[project]
name = "myproject"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"matplotlib>=3.10.8",
"numpy>=2.4.1",
]
可以看到文件中新增了 dependencies 部分,列出了 numpy 和 matplotlib。
检查锁定文件:
!cat uv.lock
uv.lock 文件记录了所有依赖包的精确版本号,确保项目在不同环境中能够复现完全相同的依赖配置。
再次执行脚本:
!uv run python main.py
现在脚本应该成功执行,生成 sine.png 图片文件。
在Jupyter中显示生成的图片:
from IPython.display import Image
Image('/content/myproject/sine.png')
开发依赖
uv支持将依赖分组管理,区分项目运行所必需的依赖和仅在开发阶段使用的依赖。开发依赖通常包括测试框架、代码格式化工具、交互式解释器等,如 ipython。
使用 uv add --dev ipython 安装开发依赖,它会被记录在 pyproject.toml 的单独分组中,不会包含在生产环境的依赖列表里。
安装后可以验证 pyproject.toml 中出现了开发依赖的配置。使用 uv run ipython 启动交互式Python环境。在ipython中执行 import sys; sys.executable 可以查看当前Python解释器的路径,确认它位于项目的虚拟环境中而非系统Python。
使用git进行版本控制
git安装
git是最流行的分布式版本控制系统,用于跟踪代码的修改历史、协作开发和管理项目版本。在Ubuntu系统下,通过系统包管理器安装git:
!sudo apt update
!sudo apt install -y git
apt update 更新软件包索引,apt install -y git 安装git,-y 选项自动确认安装提示。安装完成后验证:
!git --version
输出类似 git version 2.x.x 表示安装成功。替代方案是从官方网站 git-scm.com 下载最新版本。图形界面工具如 gitg 或 git-cola 可以辅助使用,但命令行操作是基础。
仓库初始化
git使用三个工作空间来管理文件:working directory(工作目录)是你正常编辑文件的地方;index(索引,也称暂存区)是准备提交内容的临时存放区域;history(历史,存储在.git目录中)包含所有提交记录和重建任何历史版本所需的完整信息。
从之前创建的uv项目开始,首先进入项目目录:
%cd /content/myproject
初始化git仓库:
!git init
这会在当前目录创建一个隐藏的 .git 文件夹,用于存储版本控制的所有元数据。
创建 .gitignore 文件,告诉git忽略某些不需要版本控制的文件:
%%writefile .gitignore
__pycache__/
*.pyc
.venv/
.env
__pycache__/ 是Python编译的字节码缓存目录,*.pyc 是编译后的Python文件,.venv/ 是虚拟环境目录,.env 通常存放敏感配置信息。这些都不应该纳入版本控制。
使用 git status 查看仓库状态:
!git status
此时会显示所有未被忽略的文件,验证 .gitignore 是否生效。
首次使用git需要配置用户信息,这些信息会记录在每次提交中:
!git config user.email "you@example.com"
!git config user.name "Your Name"
将所有文件添加到索引并进行第一次提交:
!git add .
!git commit -m "Initial commit: sine curve project"
git add . 将当前目录下所有文件添加到暂存区,. 表示当前目录。git commit -m 创建一个提交,-m 后面跟提交信息,描述这次修改的内容。
历史操作
确认 main.py 已被git跟踪:
!git status
修改文件,将正弦函数改为余弦函数:
%%writefile main.py
#!/usr/bin/env python3
import os
os.environ['MPLBACKEND'] = 'Agg'
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2 * np.pi, 100)
y = np.cos(x) # 改为 cos
plt.plot(x, y)
plt.title("Cosine Curve")
plt.xlabel("x")
plt.ylabel("cos(x)")
plt.savefig("cosine.png")
print("图片已保存到 cosine.png")
查看修改状态:
!git status
git会显示 main.py 已被修改但尚未暂存。
将修改添加到索引并提交:
!git add main.py
!git commit -m "Changed sine to cosine"
再次检查状态:
!git status
此时显示工作目录是干净的,所有修改都已提交。
查看提交历史:
!git log --oneline
--oneline 选项以简洁格式显示,每个提交占一行,显示简短的hash值和提交信息。
在历史中导航
首先查看提交历史,识别只包含正弦函数的那个提交的hash标识符:
!git log --oneline
输出类似:
abc1234 Changed sine to cosine
30d1937 Initial commit: sine curve project
使用 checkout 命令移动到特定提交(使用实际看到的hash值):
!git checkout 30d1937
此时工作目录的文件会回到该提交时的状态,处于"分离HEAD"状态。
验证文件内容只包含正弦计算:
!cat main.py
返回主分支:
!git checkout master
文件恢复到最新状态。
分支管理
分支允许在不影响主线代码的情况下进行独立开发。创建并切换到新分支:
!git checkout -b exponential
-b 选项表示创建新分支,exponential 是分支名称。这条命令同时完成创建和切换两个操作。
修改 main.py,将函数改为指数函数:
%%writefile main.py
#!/usr/bin/env python3
import os
os.environ['MPLBACKEND'] = 'Agg'
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2, 100)
y = np.exp(x) # 指数函数
plt.plot(x, y)
plt.title("Exponential Curve")
plt.xlabel("x")
plt.ylabel("exp(x)")
plt.savefig("exponential.png")
print("图片已保存到 exponential.png")
提交修改:
!git add main.py
!git commit -m "Changed to exponential"
返回master分支:
!git checkout master
验证master分支的文件仍然是余弦版本:
!cat main.py
这展示了分支的隔离性:不同分支可以有完全不同的代码状态,互不影响。
克隆和可重现性
git的克隆功能可以复制整个仓库,包括所有历史记录。创建一个新的工作目录:
!mkdir -p /content/another_workspace
克隆项目:
!git clone /content/myproject /content/another_workspace/myproject_clone
git clone 源路径 目标路径 会复制完整的仓库。这里是本地克隆,实际使用中通常从远程服务器(如GitHub)克隆。
进入克隆目录并执行脚本:
%cd /content/another_workspace/myproject_clone
!uv run python main.py
由于 pyproject.toml 和 uv.lock 文件也被克隆,uv会根据锁定文件安装完全相同版本的依赖,确保结果可重现。
验证结果:
from IPython.display import Image
Image('/content/another_workspace/myproject_clone/cosine.png')
返回原项目目录:
%cd /content/myproject
代码质量工具
工具概述
Python是动态类型的解释语言,这带来了高开发效率但缺少静态类型编译语言的安全保证。三个工具可以弥补这一不足:ruff 是一个极快的代码检查工具(linter),用于检测代码中的错误、潜在问题和风格违规;black 是一个代码格式化工具,自动将代码格式化为统一风格;mypy 是静态类型检查器,在不运行代码的情况下检测类型错误。
安装工具
从项目根目录,使用uv安装这些开发工具:
!uv add --dev ruff black mypy
--dev 标志表示这些是开发依赖,不会包含在生产环境中。这些工具都在 pypi.org 上可用。
验证依赖已添加到配置文件:
!cat pyproject.toml
另一种方式是使用 uv tool run 临时运行工具,这不会修改 pyproject.toml。
准备测试文件
在 src/ 目录中创建一个故意包含问题的测试文件:
!mkdir -p src
%%writefile src/quality_test.py
import math
import os
def compute(x ,y):
temp = x+ y
return temp
def PrintResult(V):
print("Result =", V )
PrintResult(compute(2,"5"))
这段代码包含多个问题:未使用的导入(math、os)、不规范的空格、函数命名不符合PEP8规范(应使用小写加下划线)、类型不匹配(整数与字符串相加)。
使用Ruff进行静态代码分析
运行ruff检查:
!uv run ruff check src/quality_test.py
默认情况下ruff只检查部分规则。要启用所有规则,在 pyproject.toml 中添加配置:
%%writefile -a pyproject.toml
[tool.ruff.lint]
select = ["ALL"]
-a 选项表示追加内容而非覆盖。再次运行检查会显示更多问题:
!uv run ruff check src/quality_test.py
使用 --fix 选项自动修正可以自动修复的问题:
!uv run ruff check --fix src/quality_test.py
ruff会自动删除未使用的导入、修正空格问题等。但某些问题(如函数命名、类型错误)需要人工修复。
使用Black进行自动格式化
black的设计理念是零配置或极少配置,它按照精心选择的规则自动格式化代码,消除团队中关于代码风格的争论。
!uv run black src/quality_test.py
black会调整缩进、空格、引号风格、行长度等,使代码符合统一标准。建议在编辑器中配置保存时自动运行black。
使用Mypy进行类型检查
运行mypy检查整个src目录:
!uv run mypy src/
mypy会分析代码中的类型注解,检测不一致的地方。对于 compute(2,"5") 这样的调用,如果函数没有类型注解,mypy可能不会报错;但如果添加了类型注解,它会检测到整数和字符串相加的问题。
类型注解修正实践
创建一个类型声明与实际使用不一致的文件:
%%writefile src/type_test.py
def scale_list_number(x: list, factor: float) -> float:
return factor * x
def display(v: float) -> None:
print(f"value is {v}")
print(scale_list_number(2, 3.0))
这段代码在运行时不会报错(Python允许数字乘以整数重复),但类型声明说参数 x 应该是 list,返回值应该是 float,而实际传入的是整数2,返回的是6.0。
使用mypy检测:
!uv run mypy src/type_test.py
mypy会指出类型不匹配的问题。
修正后的代码,使类型声明与实际使用一致:
%%writefile src/type_test.py
def scale_number(x: float, factor: float) -> float:
return factor * x
def display(v: float) -> None:
print(f"value is {v}")
result = scale_number(2.0, 3.0)
display(result)
再次验证:
!uv run mypy src/type_test.py
此时应该没有错误。运行修正后的代码验证功能正确:
!uv run python src/type_test.py
输出 value is 6.0。