当出现问题时
当 Python 无法完成你的要求时,它会引发异常:float("abc") 引发 ValueError,{}["x"] 引发 KeyError,1 / 0 引发 ZeroDivisionError,打开不存在的文件引发 FileNotFoundError。如果没有人处理,程序就会停止并打印回溯(traceback),即导致错误的调用链,从下往上读。对真正的 bug 来说,这就是正确的结果。对于可以预料的问题,比如用户输入了错误的价格,则用 try 和 except 来处理。
# Handle an expected problem
def to_price(text):
try:
return float(text)
except ValueError:
return None
print(to_price("184.25")) # 184.25
print(to_price("abc")) # None
# An unhandled one stops the program with a traceback
# print(int("abc")) # ValueError: invalid literal for int()...只捕获你预期的异常
要写明异常类型:except ValueError:。单独的 except: 或 except Exception: 还会吞掉你本不想隐藏的 bug,例如传入 None 引起的 TypeError,程序就会带着错误数据继续运行。多个类型可以共用一个处理器 except (ValueError, KeyError):,也可以分别处理。Python 按顺序尝试处理器,执行第一个匹配的。用 except ValueError as error: 可以保留错误并查看它。
# Specific handlers, tried in order
def read_quantity(text):
try:
return int(text)
except ValueError as error:
print("not a whole number:", error)
return 0
except TypeError:
print("expected text, got something else")
return 0
print(read_quantity("200")) # 200
print(read_quantity("2.5")) # prints the message, then 0
print(read_quantity(None)) # prints the message, then 0else 与 finally
语句还有两个可选部分。else: 仅在 try 块没有引发任何异常时运行,这样有风险的那一行可以保持简短,后续代码也不在保护范围内。finally: 总是运行,无论是否出错,即使在 return 之后也一样:用它做清理工作。很多时候 with 块已经替你做了清理,比如处理文件时。
# else runs on success, finally runs always
def describe(text):
try:
price = float(text)
except ValueError:
print("bad price")
else:
print("price is", price)
finally:
print("done with", repr(text))
describe("184.25")
# price is 184.25
# done with '184.25'
describe("abc")
# bad price
# done with 'abc'引发你自己的异常
当函数收到无法接受的值时,应当用 raise 明确拒绝,而不是悄悄返回错误的结果:raise ValueError("price must be positive")。选择合适的内置类型(类型对但值不好用 ValueError,类型错误用 TypeError),并写出说明问题的消息。之后由调用者决定是否捕获。在 except 块里,单独的 raise 会重新引发刚捕获的错误,当你只想记录它时很方便。
# Refuse bad input, let the caller decide
def parse_price(text):
price = float(text) # may raise ValueError itself
if not price > 0:
raise ValueError("price must be positive")
return price
for text in ["184.25", "-5", "abc"]:
try:
print(parse_price(text))
except ValueError as error:
print("rejected:", error)
# 184.25
# rejected: price must be positive
# rejected: could not convert string to float: 'abc'except(except: pass)会把本可调试的崩溃变成悄无声息的错误数字,这在交易代码里糟糕得多。只在你知道合理应对方式的地方捕获错误:使用默认值、跳过该项、重试,或报告后停止。否则就让它向上传播到知道该怎么处理的地方。try/except SpecificError:只处理你预期的问题。else在未引发异常时运行;finally总是运行。raise ValueError("message")明确拒绝无效输入。
下一节:模块与导入,如何复用标准库和你自己文件中的代码。
实验:评分内容
- 用
safe_float在不崩溃的前提下转换文本25 分 - 用
parse_price拒绝无效价格25 分 - 用
first_valid_price跳过无效条目25 分 - 用
read_quantity处理缺失或损坏的文件25 分