try-except
try-except 是 Python 捕获异常的基本结构。它让程序在出错时不崩溃,而是执行预定的错误处理逻辑。这是写出健壮代码的核心机制。
基本语法
try:
result = 10 / 0
except ZeroDivisionError:
print "Cannot divide by zero"
# 输出:Cannot divide by zero
try 块中放置可能出错的代码。如果发生 ZeroDivisionError,程序跳转到对应的 except 块执行,而不是崩溃。
捕获特定异常
可以捕获多种异常,分别处理:
try:
value = int(raw_input("Enter a number: "))
result = 100 / value
except ValueError:
print "That's not a valid number"
except ZeroDivisionError:
print "Cannot divide by zero"
异常类型按顺序匹配,第一个匹配的 except 执行,其余跳过。所以应该把最具体的异常放在前面,通用的放在后面:
try:
# 一些操作
pass
except KeyError:
print "Dictionary key not found" # 具体异常在前
except LookupError:
print "Lookup failed" # 通用异常在后(KeyError 的父类)
如果把父类放在前面,子类永远不会被匹配:
try:
d = {}
print d["missing"]
except LookupError: # 先匹配父类
print "Lookup failed"
except KeyError: # 永远不会执行!
print "Key not found"
捕获异常对象
可以用 as 获取异常对象(Python 2.6+):
try:
1 / 0
except ZeroDivisionError as e:
print "Error:", e # Error: integer division or modulo by zero
print "Type:", type(e) # <type 'exceptions.ZeroDivisionError'>
Python 2 也支持旧语法 except ZeroDivisionError, e:
try:
1 / 0
except ZeroDivisionError, e: # 旧语法,与 as 等价
print "Error:", e
逗号语法在 Python 3 中被移除,建议始终使用 as。
捕获多个异常
可以用元组同时捕获多种异常:
try:
# 可能抛出 ValueError 或 TypeError
value = int(some_input)
except (ValueError, TypeError) as e:
print "Invalid input:", e
捕获所有异常
try:
risky_operation()
except Exception as e:
print "Something went wrong:", e
Exception 是几乎所有内置非系统退出异常的基类。捕获 Exception 可以捕获大部分错误,但会隐藏 bug,应谨慎使用。
不要捕获 BaseException:
try:
risky_operation()
except BaseException: # 危险!会捕获 KeyboardInterrupt 和 SystemExit
pass
这会阻止用户用 Ctrl+C 中断程序,也会阻止 sys.exit() 正常退出。
实际应用
文件操作:
try:
with open("data.txt", "r") as f:
data = f.read()
except IOError as e:
print "Cannot read file:", e
data = ""
类型转换:
def safe_int(s, default=0):
try:
return int(s)
except (ValueError, TypeError):
return default
print safe_int("42") # 42
print safe_int("hello") # 0
print safe_int(None) # 0
嵌套异常处理:
try:
try:
1 / 0
except ZeroDivisionError:
print "Inner caught"
raise ValueError("Converted") # 重新抛出不同异常
except ValueError as e:
print "Outer caught:", e
常见错误
空的 except:
try:
1 / 0
except: # 捕获所有异常,包括 KeyboardInterrupt!
pass
空的 except: 等价于 except BaseException:,极其危险。始终指定具体的异常类型。
except 后面加条件:
try:
1 / 0
except ZeroDivisionError if x > 0: # SyntaxError!
pass
except 后面只能跟异常类型,不能加条件。需要条件判断时,在 except 块内处理。