乐趣区

关于python:翻译实用的Python编程0303Errorchecking

目录 | 上一节 (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

退出移动版