Administrator
发布于 2026-09-09 / 0 阅读
0
0

Python TP1:Linux 环境与 Python 基础

本讲义的核心目标是在受控的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 验证更改是否生效。

八进制表示法

权限除了用符号表示外,还可以用八进制数字表示。八进制值的计算公式为:

\text{octal} = (\text{用户权限}) \times 64 + (\text{组权限}) \times 8 + (\text{其他人权限}) \times 1

每种单独的权限对应一个数值:

r = 4, \quad w = 2, \quad x = 1

将需要的权限值相加即可得到一组权限的数值。例如:

rwx = 4 + 2 + 1 = 7, \quad rw\text{-} = 4 + 2 + 0 = 6, \quad r\text{-}\text{-} = 4 + 0 + 0 = 4

命令 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/zsh
  • PYTHONPATH 是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 初始化项目。

接下来创建一个使用 numpymatplotlib 绘制正弦曲线的脚本 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

此时会报错,因为虚拟环境中尚未安装 numpymatplotlib

使用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 部分,列出了 numpymatplotlib

检查锁定文件:

!cat uv.lock

uv.lock 文件记录了所有依赖包的精确版本号,确保项目在不同环境中能够复现完全相同的依赖配置。

再次执行脚本:

!uv run python main.py

现在脚本应该成功执行,生成 sine.png 图片文件。

在Jupyter中显示生成的图片:

from IPython.display import Image
Image('/content/myproject/sine.png')
image-20260119144551561
开发依赖

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 下载最新版本。图形界面工具如 gitggit-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.tomluv.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


评论