目录 | 上一节 (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()
函数容许抉择用户指定的列,然而只有输出数据文件具备列题目时才会失效。
请批改代码,以便在同时传递 select
和 has_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