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

3.3 谬误查看

尽管后面曾经介绍了异样,但本节补充一些无关谬误检查和异样解决的其它细节。

程序是如何运行失败的

Python 不对函数参数类型或值进行查看或者校验。函数能够解决与函数外部语句兼容的任何数据。

def add(x, y):    return x + yadd(3, 4)               # 7add('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 heredef spam():    grok()                        # Call that will raise exceptiondef 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)。下述列表不是一份详尽的清单,请拜访 文档 以获取更多信息。

ArithmeticErrorAssertionErrorEnvironmentErrorEOFErrorImportErrorIndexErrorKeyboardInterruptKeyErrorMemoryErrorNameErrorReferenceErrorRuntimeErrorSyntaxErrorSystemErrorTypeErrorValueError

异样值

异样具备一个关联值。它蕴含无关谬误的更明确的信息。

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