目录 | 上一节 (3.2 深入函数) | 下一节 (3.4 模块)

3.3 错误检查

虽然前面已经介绍了异常,但本节补充一些有关错误检查和异常处理的其它细节。

程序是如何运行失败的

Python 不对函数参数类型或值进行检查或者校验。函数可以处理与函数内部语句兼容的任何数据。

def add(x, y):
return x + y add(3, 4) # 7
add('Hello', 'World') # 'HelloWorld'
add('3', '4') # '34'

如果函数中有错误,它们将(作为异常)在运行时出现。

def add(x, y):
return x + y >>> add(3, '4')
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +:
'int' and 'str'
>>>

为了验证代码,强烈建议进行测试(稍后介绍)。

异常

异常用于发出错误信号。

要自己触发异常,请使用 raise 语句:

if name not in authorized:
raise RuntimeError(f'{name} not authorized')

要捕获异常,请使用 try-except 语句:

try:
authenticate(username)
except RuntimeError as e:
print(e)

异常处理

异常传递到第一个匹配的 except

def grok():
...
raise RuntimeError('Whoa!') # Exception raised here def spam():
grok() # Call that will raise exception def bar():
try:
spam()
except RuntimeError as e: # Exception caught here
... def foo():
try:
bar()
except RuntimeError as e: # Exception does NOT arrive here
... foo()

要处理异常,请将语句放到 except 块里面。 except 块里面可以添加要处理该错误的任何语句。

def grok(): ...
raise RuntimeError('Whoa!') def bar():
try:
grok()
except RuntimeError as e: # Exception caught here
statements # Use this statements
statements
... bar()

异常处理之后,从 try-except 之后的第一个语句继续执行。

def grok(): ...
raise RuntimeError('Whoa!') def bar():
try:
grok()
except RuntimeError as e: # Exception caught here
statements
statements
...
statements # Resumes execution here
statements # And continues here
... bar()

内置异常

有非常多的內建异常。通常,异常名称表明出了什么问题(例如,因为提供错误的值而触发 ValueError)。下述列表不是一份详尽的清单,请访问 文档 以获取更多信息。

ArithmeticError
AssertionError
EnvironmentError
EOFError
ImportError
IndexError
KeyboardInterrupt
KeyError
MemoryError
NameError
ReferenceError
RuntimeError
SyntaxError
SystemError
TypeError
ValueError

异常值

异常具有一个关联值。它包含有关错误的更明确的信息。

raise RuntimeError('Invalid user name')

这个值是异常实例的一部分,它被放置在提供给 except 的变量中。

try:
...
except RuntimeError as e: # `e` holds the exception raised
...

e 是异常类型的一个实例。但是,当打印的时候,它通常看起来像一个字符串。

except RuntimeError as e:
print('Failed : Reason', e)

捕获多个异常

可以使用多个 except 块捕获不同类型的异常:

try:
...
except LookupError as e:
...
except RuntimeError as e:
...
except IOError as e:
...
except KeyboardInterrupt as e:
...

或者,如果处理不同异常的语句是相同的,则可以对它们进行分组:

try:
...
except (IOError,LookupError,RuntimeError) as e:
...

捕获所有的异常

要捕获所有的异常,请使用 Exception 。如下所示:

try:
...
except Exception: # DANGER. See below
print('An error occurred')

通常,像这样编写代码是个坏主意,因为这说明不知道程序为什么会失败。

捕获异常的错误方式

这里是一个使用异常的错误方式。

try:
go_do_something()
except Exception:
print('Computer says no')

这将捕获所有可能的错误,并且,当代码因为某些根本没想到的原因(如卸载 Python 模块等)运行失败时,可能无法进行调试。

更好的方式

如果想要捕获所有的错误,这有一个更明智的方法。

try:
go_do_something()
except Exception as e:
print('Computer says no. Reason :', e)

它报告了失败的明确原因。当编写捕获所有可能异常的代码时,拥有查看/报告错误的机制几乎总是一个好主意。

不过,通常来说,最好在合理的范围内尽量窄地捕获异常。仅捕获能处理的异常。让其它错误通过——也许其它代码可以处理。

重新触发异常

使用 raise 传递捕获的错误。

try:
go_do_something()
except Exception as e:
print('Computer says no. Reason :', e)
raise

这允许你采取措施(例如:记录日志)并将错误传递给调用者。

异常的最佳实践

不要捕获异常,而是失败发生时“停止运行,发出预警”(Fail fast and loud)。如果重要的话,别人会处理的。只有你是那个人的时候才捕获异常。即,只捕获可以恢复并正常运行的错误。

finally 语句

finally 语句指定无论是否发生异常都必须运行的代码。

lock = Lock()
...
lock.acquire()
try:
...
finally:
lock.release() # this will ALWAYS be executed. With and without exception.

通常使用 finally 语句安全地管理资源(尤其是锁,文件等)。

with 语句

在现代代码中,try-finally 语句通常被 with 语句取代。

lock = Lock()
with lock:
# lock acquired
...
# lock released

一个更熟悉的例子:

with open(filename) as f:
# Use the file
...
# File closed

with 语句定义资源的使用上下文。当执行离开上下文时,资源被释放。with 语句仅适用于经过专门编程以支持它的某些对象。

练习

练习 3.8:触发异常

在上一节中编写的 parse_csv() 函数允许选择用户指定的列,但是只有输入数据文件具有列标题时才会生效。

请修改代码,以便在同时传递 selecthas_headers=False 参数时触发异常。例如:

>>> parse_csv('Data/prices.csv', select=['name','price'], has_headers=False)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "fileparse.py", line 9, in parse_csv
raise RuntimeError("select argument requires column headers")
RuntimeError: select argument requires column headers
>>>

添加此检查后,你可能会问是否应该在函数中执行其它类型的完整性检查。例如,检查文件名是字符串,列表还是其它类型?

一般来说,最好是跳过此类测试,输入错误的时候让程序运行失败。回溯信息会指出问题的根源,并且帮助调试。

添加上述检查的主要原因是为了避免在无意义的模式下运行代码(例如,使用要求列标题的特性,但是同时指定没有标题)。

这表明调用代码部分出现一个编程错误。检查“不应发生”的情况通常是个好主意。

练习 3.9:捕获异常

你编写的 parse_csv() 函数用于处理文件的全部内容。但是,在现实世界中,输入文件可能包含损坏的数据,丢失的数据或者脏数据。尝试下面这个实验:

>>> portfolio = parse_csv('Data/missing.csv', types=[str, int, float])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "fileparse.py", line 36, in parse_csv
row = [func(val) for func, val in zip(types, row)]
ValueError: invalid literal for int() with base 10: ''
>>>

请修改 parse_csv() 函数以便捕获所有在记录创建期间生成的 ValueError 异常,并为无法转换的行打印警告消息。

错误消息应该包括行号以及有关失败原因的信息。要测试函数,尝试读取上面的 Data/missing.csv 文件,例如:

>>> portfolio = parse_csv('Data/missing.csv', types=[str, int, float])
Row 4: Couldn't convert ['MSFT', '', '51.23']
Row 4: Reason invalid literal for int() with base 10: ''
Row 7: Couldn't convert ['IBM', '', '70.44']
Row 7: Reason invalid literal for int() with base 10: ''
>>>
>>> portfolio
[{'price': 32.2, 'name': 'AA', 'shares': 100}, {'price': 91.1, 'name': 'IBM', 'shares': 50}, {'price': 83.44, 'name': 'CAT', 'shares': 150}, {'price': 40.37, 'name': 'GE', 'shares': 95}, {'price': 65.1, 'name': 'MSFT', 'shares': 50}]
>>>

练习 3.10:隐藏错误

请修改 parse_csv()函数,以便用户明确需要时可以隐藏解析的错误消息,例如:

>>> portfolio = parse_csv('Data/missing.csv', types=[str,int,float], silence_errors=True)
>>> portfolio
[{'price': 32.2, 'name': 'AA', 'shares': 100}, {'price': 91.1, 'name': 'IBM', 'shares': 50}, {'price': 83.44, 'name': 'CAT', 'shares': 150}, {'price': 40.37, 'name': 'GE', 'shares': 95}, {'price': 65.1, 'name': 'MSFT', 'shares': 50}]
>>>

在大部分的程序中,错误处理是最难做好的事情之一。一般来说,不应该默默地忽略错误。相反,最好是报告问题,并且让用户选择是否隐藏错误信息(如果它们选择这样做)。

目录 | 上一节 (3.2 深入函数) | 下一节 (3.4 模块)

注:完整翻译见 https://github.com/codists/practical-python-zh

翻译:《实用的Python编程》03_03_Error_checking的更多相关文章

  1. 翻译:《实用的Python编程》InstructorNotes

    实用的 Python 编程--讲师说明 作者:戴维·比兹利(David Beazley) 概述 对于如何使用我的课程"实用的 Python 编程"进行教学的问题,本文档提供一些通用 ...

  2. 翻译:《实用的Python编程》README

    欢迎光临 大约 25 年前,当我第一次学习 Python 时,发现 Python 竟然可以被高效地应用到各种混乱的工作项目上,我立即被震惊了.15 年前,我自己也将这种乐趣教授给别人.教学的结果就是本 ...

  3. 翻译:《实用的Python编程》05_02_Classes_encapsulation

    目录 | 上一节 (5.1 再谈字典) | 下一节 (6 生成器) 5.2 类和封装 创建类时,通常会尝试将类的内部细节进行封装.本节介绍 Python 编程中有关封装的习惯用法(包括私有变量和私有属 ...

  4. 翻译:《实用的Python编程》04_02_Inheritance

    目录 | 上一节 (4.1 类) | 下一节 (4.3 特殊方法) 4.2 继承 继承(inheritance)是编写可扩展程序程序的常用手段.本节对继承的思想(idea)进行探讨. 简介 继承用于特 ...

  5. 翻译:《实用的Python编程》01_02_Hello_world

    目录 | 上一节 (1.1 Python) | 下一节 (1.3 数字) 1.2 第一个程序 本节讨论有关如何创建一个程序.运行解释器和调试的基础知识. 运行 Python Python 程序始终在解 ...

  6. 翻译:《实用的Python编程》03_04_Modules

    目录 | 上一节 (3.3 错误检查) | 下一节 (3.5 主模块) 3.4 模块 本节介绍模块的概念以及如何使用跨多个文件的函数. 模块和导入 任何一个 Python 源文件都是一个模块. # f ...

  7. 翻译:《实用的Python编程》03_05_Main_module

    目录 | 上一节 (3.4 模块) | 下一节 (3.6 设计讨论) 3.5 主模块 本节介绍主程序(主模块)的概念 主函数 在许多编程语言中,存在一个主函数或者主方法的概念. // c / c++ ...

  8. 翻译:《实用的Python编程》04_01_Class

    目录 | 上一节 (3.6 设计讨论) | 下一节 (4.2 继承) 4.1 类 本节介绍 class 语句以及创建新对象的方式. 面向对象编程(OOP) 面向对象编程是一种将代码组织成对象集合的编程 ...

  9. 翻译:《实用的Python编程》05_00_Overview

    目录 | 上一节 (4 类和对象) | 下一节 (6 生成器) 5. Python 对象的内部工作原理 本节介绍 Python 对象的内部工作原理.来自其它语言的程序员通常会发现 Python 的类概 ...

随机推荐

  1. Codeforces Round #675 (Div. 2)【ABCD】

    比赛链接:https://codeforces.com/contest/1422 A. Fence 题意 给出三条边 $a,b,c$,构造第四条边使得四者可以围成一个四边形. 题解 $d = max( ...

  2. F - To Add Which?

    Description There is an integer sequence with N integers. You can use 1 unit of cost to increase any ...

  3. Checkout Assistant CodeForces - 19B

    题意: 给你n个物品,每个物品有一个价格ci和一个支付时间ti,在这个ti时间内,你可以免费拿ti个物品.问你想要带走这n个物品最小需要多少钱 题解: 原本还想着贪心去写,但是好像贪心写不了,,,不属 ...

  4. Python+Appium实现自动抢微信红包

    前言 过年的时候总是少不了红包,不知从何时开始微信红包横空出世,对于网速和手速慢的人只能在一旁观望,做为python的学习者就是要运用编程解决生活和工作上的事情. 于是我用python解决我们的手速问 ...

  5. next v5升级到next v7需要注意的地方

    title: next v5升级到next v7需要注意的地方 date: 2020-03-04 categories: web tags: [hexo,next] 大部分的设置都是一样的,但有一些细 ...

  6. IFIX 目录结构

    iFIX使用了许多目录存储程序和数据文件.用路径配置对话框来指定iFIX目录的位置和名称.可在SCU工具箱中单击"路径"按钮显示该对话框. 当iFIX安装后它将创建一个目录,该目录 ...

  7. Netty(六)揭开 BootStrap 的神秘面纱

    6.1 客户端 BootStrap 6.1.1 Channel 简介 在 Netty 中,Channel 是一个 Socket 的抽象,它为用户提供了关于 Socket 状态(是否是连接还是断开)以及 ...

  8. IDEA插件:快速删除Java代码中的注释

    背景   有时,我们需要删除Java源代码中的注释.目前有不少方法,比如: 实现状态机.该方式较为通用,适用于多种语言(取决于状态机支持的注释符号). 正则匹配.该方式容易误判,尤其是容易误删字符串. ...

  9. php foundation knowledge!

    php foundation knowledge! 1 <?php 2 $p = "PII"; 3 define("XPI",3.1415926); 4 ...

  10. js location API All In One

    js location API All In One location "use strict"; /** * * @author xgqfrms * @license MIT * ...