飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 第1章 Python简介

    • Python是什么
    • 安装与运行
    • 交互式解释器
    • 注释与编码规范
  • 第2章 变量与数据类型

    • 变量与对象
    • 整数 int
    • 浮点数 float
    • 复数 complex
    • 布尔值 bool
    • 字符串 str
    • 空值 None
    • 类型转换
  • 第3章 运算符与表达式

    • 算术运算符
    • 比较运算符
    • 赋值运算符
    • 逻辑运算符
    • 位运算符
    • 身份与成员运算符
    • 海象运算符
    • 运算符优先级
  • 第4章 流程控制

    • if 语句
    • if-else 语句
    • if-elif-else 语句
    • match-case 语句
    • 条件表达式(三元运算符)
    • while 循环
    • for 循环
    • range 函数
    • break 与 continue
    • 循环的 else 子句
    • pass 语句
  • 第5章 数据结构

    • 列表创建与索引
    • 列表方法
    • 列表推导式
    • 元组
    • 序列解包
    • 集合
    • 字典创建与访问
    • 字典方法
    • 字典推导式
    • range 对象
  • 第6章 函数

    • 定义函数
    • 位置参数与关键字参数
    • 默认参数
    • 可变参数
    • 解包实参
    • 函数返回值
    • lambda 表达式
    • 文档字符串与注解
    • 作用域与命名空间
    • global 与 nonlocal
  • 第7章 模块与包

    • 模块导入
    • 模块搜索路径
    • 包与相对导入
    • 标准库概览
  • 第8章 文件与输入输出

    • 文件读写
    • 上下文管理器
    • 字符串格式化
    • JSON 与 CSV
  • 第9章 面向对象

    • 类与对象
    • 方法
    • 实例变量与类变量
    • 私有变量
    • 继承
    • 多重继承
    • 魔术方法
    • 属性装饰器
    • 数据类 dataclass
  • 第10章 异常处理

    • 语法错误与异常
    • try-except
    • 异常链与 raise
    • 清理操作
    • 自定义异常
  • 第11章 迭代器与生成器

    • 迭代器协议
    • 生成器
    • 生成器表达式
    • 迭代工具
  • 第12章 高级特性

    • 装饰器
    • 函数式编程
  • 第13章 工程实践

    • 测试与调试
    • 代码质量
    • 虚拟环境

语法错误与异常

Python 程序中的问题可分为两类:语法错误在代码执行前就被解析器发现,异常则在代码运行期间触发。理解二者的区别是调试的第一步。

语法错误(SyntaxError)

语法错误又称解析错误,是代码不符合 Python 语法规则导致的。这类错误在程序启动时就会被拦截,根本不会进入执行阶段。

>>> while True print('hello')
  File "<stdin>", line 1
    while True print('hello')
               ^^^^^
SyntaxError: invalid syntax

解析器会打印出错的行,并用 ^ 标记检测到错误的位置。注意箭头指向的位置不一定是实际要修复的地方——上例中错误在 print() 处被检测到,真正的原因是前面缺少冒号。语法错误无法通过 try/except 捕获,因为它发生在代码编译阶段,而非运行时。

常见的语法错误触发场景包括:括号不匹配、缩进错误(IndentationError,它是 SyntaxError 的子类)、保留字误用、赋值号与等于号混淆等。

# 括号不匹配
print("hello"  # SyntaxError: unexpected EOF while parsing

# 缩进错误
def foo():
print("no indent")  # IndentationError

# 保留字误用
class = 5  # SyntaxError: invalid syntax

异常(Exception)

即使语法完全正确,语句执行时仍可能出错。这种运行时检测到的错误称为异常。异常不一定会导致程序崩溃——它们可以被捕获和处理。

>>> 10 * (1 / 0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero

>>> 4 + spam * 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'spam' is not defined

>>> '2' + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str

异常信息由三部分组成:

  1. Traceback 堆栈回溯:以 Traceback (most recent call last): 开头,列出异常发生时的调用链,从最早进入的函数到最终触发异常的代码行。
  2. 异常类型:如 ZeroDivisionError、NameError、TypeError。标准异常类型是内置标识符,不是保留关键字。
  3. 异常详情:类型名后面的字符串,描述具体错误原因,如 division by zero。

常见内置异常

Python 内置了丰富的异常体系,覆盖绝大多数运行时错误场景:

异常触发场景示例
ZeroDivisionError除数为零1 / 0
NameError引用未定义的变量print(x) 而 x 未赋值
TypeError操作或函数应用于不适当类型的对象len(5)、'2' + 2
ValueError类型正确但值不合法int("abc")
IndexError序列索引越界[1,2][5]
KeyError字典中找不到键d['missing']
AttributeError对象没有该属性[].missing
FileNotFoundError文件不存在open('no.txt')
OSError系统级操作失败文件、网络、权限错误
RuntimeError不属于其他类别的错误通用运行时错误
RecursionError递归过深无限递归
MemoryError内存耗尽创建超大对象
KeyboardInterrupt用户按 Ctrl+C中断程序
SystemExitsys.exit() 被调用主动退出程序
# 类型错误:操作数类型不匹配
try:
    result = '年龄:' + 25
except TypeError as e:
    print(f"类型错误:{e}")
    # 修正:result = '年龄:' + str(25)

# 值错误:类型对但值不合法
try:
    age = int("不是数字")
except ValueError as e:
    print(f"值错误:{e}")

# 索引错误
try:
    data = [1, 2, 3]
    print(data[10])
except IndexError as e:
    print(f"索引越界:{e}")

# 键错误
try:
    config = {"host": "localhost"}
    print(config["port"])
except KeyError as e:
    print(f"缺少配置项:{e}")

异常层次结构

所有异常都继承自 BaseException,但日常编程中更关注它的子类 Exception。Exception 是所有非致命异常的基类,而 SystemExit、KeyboardInterrupt 等直接继承 BaseException 的异常通常表示程序应当终止,一般不被捕获。

BaseException
 ├── SystemExit          # sys.exit() 触发,正常退出
 ├── KeyboardInterrupt   # Ctrl+C 触发
 ├── GeneratorExit       # 生成器关闭时触发
 └── Exception           # 所有常规异常的基类
      ├── ArithmeticError
      │    └── ZeroDivisionError
      ├── LookupError
      │    ├── IndexError
      │    └── KeyError
      ├── TypeError
      ├── ValueError
      ├── AttributeError
      └── OSError
           ├── FileNotFoundError
           ├── PermissionError
           └── IsADirectoryError

这个层次结构决定了 except 子句的匹配规则:捕获父类时,也会捕获其所有子类。例如 except OSError 会同时捕获 FileNotFoundError 和 PermissionError。

# 利用层次结构进行分级处理
try:
    with open("data.txt") as f:
        content = f.read()
        number = int(content)
        result = 100 / number
except FileNotFoundError:
    print("文件不存在,使用默认配置")
except PermissionError:
    print("权限不足,无法读取文件")
except OSError as e:
    print(f"其他系统错误:{e}")  # 捕获除上述两种外的 OSError 子类
except ValueError:
    print("文件内容不是有效数字")
except ZeroDivisionError:
    print("文件中数字为零,无法做除法")

阅读 Traceback

Traceback 是定位问题的关键线索。阅读时应从底部向上看:最下面一行是异常类型和消息,往上是发生异常的代码位置,再往上是调用该代码的函数,依此类推。

def read_config(path):
    with open(path) as f:
        return int(f.read().strip())

def calculate_rate(config_path):
    total = read_config(config_path)
    return 1000 / total

def main():
    rate = calculate_rate("config.txt")
    print(f"汇率:{rate}")

main()

如果 config.txt 内容为 0,Traceback 如下:

Traceback (most recent call last):
  File "demo.py", line 12, in <module>
    main()
  File "demo.py", line 10, in main
    rate = calculate_rate("config.txt")
  File "demo.py", line 7, in calculate_rate
    return 1000 / total
ZeroDivisionError: division by zero

阅读顺序:

  1. 最底部:ZeroDivisionError: division by zero —— 知道是除零错误。
  2. File "demo.py", line 7 —— 错误发生在 calculate_rate 函数的第 7 行 return 1000 / total。
  3. 再往上 —— calculate_rate 是被 main() 调用的,main() 又在模块顶层被调用。

这样能快速定位到问题根源:total 的值为 0,而 total 来自 read_config 读取的文件内容。

异常参数

异常被触发时可以携带参数,即异常对象关联的值。参数类型和数量取决于异常类型。通过 as 绑定可以访问异常实例,其 .args 属性以元组形式保存所有参数。

try:
    raise ValueError("年龄不能为负数", "age", -5)
except ValueError as e:
    print(type(e))      # <class 'ValueError'>
    print(e.args)       # ('年龄不能为负数', 'age', -5)
    print(e)            # 年龄不能为负数
    msg, field, val = e.args
    print(f"字段 {field} 的值 {val} 不合法:{msg}")

内置异常类型通常定义了 __str__() 方法,因此直接打印异常实例会显示第一个参数,无需手动访问 .args。自定义异常可以覆盖这一行为,提供更友好的错误信息。

下一页
try-except