(翻译)30天学习Python👨‍💻第十四天——错误处理

345 阅读6分钟

30天学习Python👨‍💻第十四天——错误处理

错误处理

没有程序是完美的,除非是一个精心编写的hello world程序 😃。

今天我要探讨的是Python中错误处理的全部内容,我尽量用平实简单的话来解释。错误处理可能是程序中最重要的概念了。

所有的应用都可能出现bug或者异常,错误是难免的。当程序由于某种未知的原因运行或执行失败时,错误通常会传递有用的信息。这可能是一个偶然的捆绑错误,可能是应用程序离线却试图访问互联网。可能是计算错误或者程序运行导致内存溢出。在我们写程序时,有很多可能出现错误。当错误发生时,程序就会停止运行,这不是我们所想要的。与其期望程序运行没有错误,更好更实用的做法是捕获错误并进行相应的处理。

在Python中,错误大致可能分为两类 —— 语法错误(Syntax Errors)和程序异常(Exceptions)。

当我们写的Python代码编译器无法确定是正确的(符合规则)代码时,就会出现语法错误。

print('A grave mistake) # 少了一个引号,会出现语法错误
def hello # 少了 :, 会出现语法错误
    print('hello')

无论何时程序运行出现了语法错误,Python编译器都能够给出关于是哪一行代码造成了错误的详细信息。

有意义的是一些其他的错误会导致另一部分的语法错误。

我们的程序经常要接受外部的值,并且计算的执行要依赖于这些值。但这些值可能经常是不正确的。在这种情况下,就会出现错误。因此,它们需要被处理。

age = input('Enter your age')
if age > 18:
  print('You are an adult')
else:
  print('You are a minor')

运行这段简单的代码会出现异常

Traceback (most recent call last):
  File "main.py", line 2, in <module>
    if age > 18:
TypeError: '>' not supported between instances of 'str' and 'int'

这个异常是一个类型错误,因为age的值是一个字符串,所以编译器阻止了程序的运行,age需要类型转换或者转换为一个数值。

age = int(input('Enter your age'))
if age > 18:
  print('You are an adult')
else:
  print('You are a minor')

现在,如果用户给的是一个字符串而不是一个数值,将会抛出另一个异常

Enter your age asdf
Traceback (most recent call last):
  File "main.py", line 1, in <module>
    age = int(input('Enter your age'))
ValueError: invalid literal for int() with base 10: 'asdf'

Try except语句块

为了阻止类似的错误,Python提供了try``except语句块,如果try语句块中捕获到了一个错误,将会执行except代码块。

作为对比,JavaScript中,使用的是try``catch语句块。Python只是catch的名字不一样,但功能是相似的。

try:
  age = int(input('Enter your age'))
  if age > 18:
    print('You are an adult')
  else:
    print('You are a minor')
except:
  print('Invalid value provided')

Else语句块

除了try except语句块,Python还提供了else,如果在try``except语句块没有捕获到异常的时候就执行else语句块中。这是try except语句的扩展。

try:
  age = int(input('Enter your age'))
  if age > 18:
    print('You are an adult')
  else:
    print('You are a minor')
except:
  print('Invalid value provided')
else:
  print('Thank You!')

Finally语句块

在程序开发中经常我们会想无论是否有异常都会执行一个动作。比如说向服务器发送日志信息。Python提供了finally块作为try except块的一部分,无论是否有异常都会执行它。

try:
  age = int(input('Enter your age'))
  if age > 18:
    print('You are an adult')
  else:
    print('You are a minor')
except:
  print('Invalid value provided')
finally:
  print('Sendiing dummy log to server') # 总是会被打印

内置异常

Python提供了许多内置的异常,它们都继承自BaseException

Python内置异常

这些异常类能够用于处理特殊的异常,比如TypeError, ValueError, SystemError

from functools import reduce

def calc_average(number_list):
  '''
  接受一个列表并且返回平均值
  '''
  sum = reduce(lambda acc, curr: acc + curr, number_list)
  average = sum/len(number_list)
  return average

print(calc_average([1,2,3,4,5])) # 3.0

# 传入非法参数时将会报错
print(calc_average(['1','2','3','4','5'])) # TypeError
print(calc_average(None)) # TypeError
print(calc_average(3/0)) # ZeroDivisionError

为了使函数更加可靠,我们可以捕获异常

from functools import reduce

def calc_average(number_list):
  '''
  接受一个列表并且返回平均值
  '''
  try:
    sum = reduce(lambda acc, curr: acc + curr, number_list)
    average = sum/len(number_list)
    return average
  except TypeError:
    print('Only a list of numbers is valid')


print(calc_average('hello world')) # 输出:Only a list of numbers is valid

上面的代码捕获了非法参数。但是代码逻辑本身可能存在缺陷,可以使用单独的except块处理。

from functools import reduce

def calc_average(number_list):
  '''
  接受一个列表并且返回平均值
  '''
  try:
    sum = reduce(lambda acc, curr: acc + curr, number_list)
    average = sum/len(number_list)
    1.0/0 # Bug
    return average
  except TypeError:
    print('Only a list of numbers is valid')
  except ZeroDivisionError:
    print('cannot divide by zero')


print(calc_average([1,2,3,4,5])) # cannot divide by zero'

多个异常也能够在一条语句中被同时捕获

from functools import reduce

def calc_average(number_list):
  '''
  接受一个列表并且返回平均值
  '''
  try:
    sum = reduce(lambda acc, curr: acc + curr, number_list)
    average = sum/len(number_list)
    return average
  except (TypeError, ZeroDivisionError): # 处理多种情况
    print('Only a list of numbers is valid')

print(calc_average(['asdfasdf'])) # Only a list of numbers is valid

上面的代码处理可能的异常,但是隐藏了编译器提供的有用堆栈错误,这些信息能够告诉我们错误是哪一行代码造成的。

实际的错误消息和自定义的错误消息可以组合在一起。

from functools import reduce

def calc_average(number_list):
  '''
  接受一个列表并且返回平均值
  '''
  try:
    sum = reduce(lambda acc, curr: acc + curr, number_list)
    average = sum/len(number_list)
    return average
  except TypeError as type_error:
    print(f'Only a list of numbers is valid {type_error}')
  except ZeroDivisionError as zero_div_error:
    print(f'cannot divide by zero {zero_div_error}')  

print(calc_average('hello world')) 
# Only a list of numbers is valid unsupported operand type(s) for /: 'str' and 'int'

这能使错误信息更有用。在Pytho中使用raise关键字也可以根据某些条件引发自定义错误

name = input('Enter your name')
if name.lower() == 'god':
  raise('Name cannot be GOD!')
else:
  print(name)

记住Python中所有的内置错误处理类是不可能的,除非你是专家!

Python内置异常文档可以作为处理所需异常的参考。

参考:

  1. realpython.com/python-exce…
  2. www.programiz.com/python-prog…
  3. docs.python.org/3/tutorial/…

这就是今天所有的内容。这是一个重要的主题,它能够帮助我们编写Python代码更具弹性。明天我将会探讨另一个Python主题——生成器。

原文链接

dev.to/arindamdawn…