Skip to content

sys 模块 ​

命令行程序经常需要确认运行环境、接收参数并把错误交给调用方。如何知道当前使用的解释器,如何区分正常输出和错误输出,又如何返回执行结果?sys 模块用于和当前 Python 解释器交互,可以集中解决这些问题。

它和上一章的 os 模块关注点不同:

模块主要职责
os文件、目录和操作系统功能
sysPython 解释器和当前运行环境

本章将使用 sys 编写一个简单的命令行程序。

sys 命令行程序流程

解释器环境 ​

排查“为什么程序运行结果不一样”时,首先要确认使用的解释器版本和位置。

sys.executable ​

sys.executable 是当前 Python 解释器的路径:

python
import sys

print(sys.executable)

在编辑器、虚拟环境和命令行使用不同解释器时,这个值也会不同。它可以帮助我们确认程序实际由哪个 Python 运行。

sys.version_info ​

sys.version_info 保存当前 Python 的版本信息:

python
import sys

print(sys.version_info)
print(sys.version_info.major)
print(sys.version_info.minor)

需要判断版本时,直接比较版本信息,不要解析版本字符串:

python
import sys

if sys.version_info < (3, 14):
    print("当前程序需要 Python 3.14 或更高版本")

本课程示例默认使用 Python 3.14。

sys.platform ​

sys.platform 表示当前运行平台:

python
import sys

print(sys.platform)

常见结果如下:

系统常见值
Windowswin32
Linuxlinux
macOSdarwin

即使运行的是 64 位 Windows,结果通常仍然是 win32。它是平台标识,不表示处理器位数。

命令行参数 ​

sys.argv 是一个列表,保存启动程序时传入的命令行参数。

创建 show_args.py:

python
import sys

print(sys.argv)

在命令行中运行:

shell
python3 show_args.py 张三 85

一次运行结果:

text
['show_args.py', '张三', '85']

其中:

  • sys.argv[0] 表示程序入口,其内容会受到启动方式影响。
  • sys.argv[1] 开始才是传给程序的参数。
  • 命令行参数全部是字符串,数字需要自行转换。

参数数量校验 ​

直接读取不存在的下标会抛出 IndexError。使用参数前应该先检查列表长度:

python
import sys

if len(sys.argv) != 3:
    print("用法:python3 show_args.py <姓名> <成绩>")
else:
    name = sys.argv[1]
    score_text = sys.argv[2]
    print(name, score_text)

参数数量正确之后,还要根据业务要求检查参数格式和取值范围。

程序退出状态 ​

sys.exit() 会结束当前程序。传入的整数称为退出状态:

  • 0 通常表示程序成功结束。
  • 非 0 通常表示程序遇到错误。
python
import sys

if len(sys.argv) != 2:
    print("参数数量错误")
    sys.exit(2)

print(f"收到参数:{sys.argv[1]}")

sys.exit() 执行后,后面的代码不会继续运行。它实际上会抛出 SystemExit,由 Python 解释器把退出状态返回给操作系统。

更完整的程序通常让 main() 返回状态码,再统一退出:

python
import sys


def main():
    if len(sys.argv) != 2:
        return 2

    print(f"收到参数:{sys.argv[1]}")
    return 0


if __name__ == "__main__":
    sys.exit(main())

这样能够把业务流程与程序退出动作分开。

标准流 ​

命令行程序通常使用三条标准流:

标准流主要用途
sys.stdin接收输入
sys.stdout输出正常结果
sys.stderr输出警告和错误

sys 三标准流与重定向机制

input() 默认从标准输入读取,print() 默认把内容写到标准输出:

python
name = input("请输入姓名:")
print(f"你好,{name}")

需要直接读取一行时,可以使用 sys.stdin.readline():

python
import sys

line = sys.stdin.readline().rstrip("\n")
print(f"读取内容:{line}")

这里使用 rstrip("\n") 只移除末尾换行符,不会无条件删除正常字符。

错误输出 ​

错误信息应该写入标准错误流:

python
import sys

print("错误:成绩必须是整数", file=sys.stderr)

正常结果和错误信息使用不同标准流后,命令行工具和自动化脚本就能分别处理它们。

输出重定向 ​

不要为了写入文件而直接替换 sys.stdout。可以把文件对象传给 print():

python
with open("result.txt", "w", encoding="utf-8") as file:
    print("程序运行结果", file=file)

这种写法只影响当前这一次输出,不会改变整个程序的标准输出。程序运行日志将在下一章使用 logging 模块处理。

模块运行状态 ​

模块章节已经介绍过搜索路径和导入缓存。sys 提供了对应的运行时信息。

sys.modules 缓存与 sys.path 搜索链双引擎

sys.path ​

sys.path 保存模块搜索路径:

python
import sys

for path in sys.path:
    print(repr(path))

路径内容会受到解释器安装位置、虚拟环境和启动方式影响,因此不应该把某台计算机上的固定输出写进程序。

列表中的空字符串表示按当前工作目录搜索。第一项并不永远是空字符串:直接运行脚本、执行模块或进入交互环境时,都可能得到不同结果。

一般项目不应通过 sys.path.append() 临时修补导入问题。优先使用清楚的包结构和正确的程序入口。

sys.modules ​

sys.modules 是一个字典,保存当前进程已经加载的模块:

python
import math
import sys

print("math" in sys.modules)
print(sys.modules["math"])

导入模块时,Python 会先检查这份缓存。同一个进程中再次导入已加载模块时,通常会直接复用模块对象。

不要手动修改 sys.modules。基础阶段只需要用它观察模块是否已经加载。

运行环境编码 ​

标准流和文件系统都可能涉及编码。可以查看当前运行环境的实际配置:

python
import sys

print(sys.stdin.encoding)
print(sys.stdout.encoding)
print(sys.stderr.encoding)
print(sys.getfilesystemencoding())

这些结果会受到终端、操作系统和启动配置影响。读写文本文件时仍然应该主动指定 encoding="utf-8",不要只依赖环境默认值。

综合案例:命令行成绩检查 ​

需求: 从命令行接收姓名和成绩,校验成绩范围,输出等级,并通过退出状态告知调用方执行结果。

功能分析: 使用 sys.argv 读取参数,使用 sys.stderr 输出输入错误,使用 sys.executable 生成用法提示,使用 sys.exit() 返回状态码。

下面的程序从命令行接收姓名和成绩,并根据检查结果返回不同退出状态:

python
import sys


def parse_score(score_text):
    try:
        score = int(score_text)
    except ValueError:
        print("错误:成绩必须是整数", file=sys.stderr)
        return None

    if not 0 <= score <= 100:
        print("错误:成绩必须在 0~100 之间", file=sys.stderr)
        return None

    return score


def get_level(score):
    if score >= 90:
        return "优秀"
    if score >= 80:
        return "良好"
    if score >= 60:
        return "及格"
    return "不及格"


def main():
    if len(sys.argv) != 3:
        print(
            f"用法:{sys.executable} {sys.argv[0]} <姓名> <成绩>",
            file=sys.stderr,
        )
        return 2

    name = sys.argv[1]
    score = parse_score(sys.argv[2])

    if score is None:
        return 2

    print(f"{name}:{score} 分,等级为{get_level(score)}")
    return 0


if __name__ == "__main__":
    sys.exit(main())

传入正确参数:

shell
python3 main.py 张三 85

运行结果:

text
张三:85 分,等级为良好

没有传入参数、成绩不是整数或成绩超出范围时,程序会把错误写入 stderr,并返回状态码 2。

这个案例串联了本章的核心内容:

  1. 使用 sys.argv 接收参数。
  2. 使用 sys.stderr 输出错误。
  3. 使用 sys.executable 显示当前解释器。
  4. 使用 sys.exit() 返回退出状态。

小结 ​

  • sys.executable 和 sys.version_info 用于确认解释器环境。
  • sys.argv 保存命令行参数,其中的参数都是字符串。
  • sys.exit() 可以结束程序,并向操作系统返回退出状态。
  • stdin、stdout 和 stderr 分别负责标准输入、正常输出和错误输出。
  • sys.path 保存模块搜索路径,sys.modules 保存已加载模块。
  • 运行环境编码可能不同,读写文本文件时应主动指定编码。

练习 ​

  1. 输出当前解释器路径、Python 主版本号、次版本号和系统平台。
  2. 编写一个命令行加法程序,接收两个数字并输出计算结果。
  3. 参数数量或数字格式错误时,把提示写入 stderr 并返回非零状态。
  4. 导入一个标准库模块,再通过 sys.modules 确认它已经加载。
  5. 输出当前标准输出编码和文件系统编码,比较两者是否相同。