第八章 异常

一、错误与异常
1. 语法错误:

Python的语法错误或者称之为解析错误:

while True print('Hello world')
  File "<stdin>", line 1, in ?
    while True print('Hello world')
                   ^
SyntaxError: invalid syntax
这个例子中,函数print()被检查到有错误,是它前面缺少了一个冒号(:),语法分析器指出了出错的一行,并且在最先找到的错误位置标记了一个小小的箭头。  
2. 异常

即使Python程序的语法是正确的,在运行时,也有可能发生错误。运行期检测到的错误称为异常,大多数异常都不会被程序处理,以错误信息的形式展现在这里。

>>>10 * (1/0)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: Can't convert 'int' object to str implicitly

Python使用异常对象来表示异常状态,并在遇到错误时引发异常。异常对象未被处理(或捕获)时,程序将终止并显示一条错误信息(Traceback…) 也就是程序出现错误时报告的语句。

3. 异常类型

内置异常类型:

  • Exception:几乎所有的异常类都是从它派生而来的
  • AttributeError:引发属性或给它赋值失败时引发
  • OSError:操作系统不能执行指定的任务(如打开文件)时引发,有多个子类
  • IndexError:使用序列中不存在的索引时引发,为LookupError的子类
  • KeyError:使用映射中不存在的键时引发,为LookupError的子类
  • NameError:找不到名称或变量时引发
  • SyntaxError:代码不正确时引发
  • TypeError:将内置操作或函数用于类型不正确的对象时引发
  • ValueError:将内置操作或函数用于这样的对象时引发:其类型正确但包含的值不合适
  • ZeroDivisionError:在除法或求模运算的第二个参数为零时引发
  • TypeError:不同类型间的无效操作,有些类型不同是不能相互进行计算的,否则会抛出TypeError异常。
    自定义异常类
    创建方法与创建类相似
    class SomeCustomException(Exception):pass
4. 异常处理

语句格式如下:

try:
    检测范围
except Exception[as reason]:
    出现异常(Exception)后的处理代码
while True:
    try:
        x=int(input('please enter a number:')
        break
    except ValueError:
        print('Oops! That was no valid number. Try again')

try语句按照以下方式工作:

  • 首先,执行try子句(在关键字try和关键字except之间的语句)
  • 如果没有异常,忽略except子句,try子句执行后结束。
  • 如果在执行try子句的过程中发生了异常,那么try子句余下的部分将被忽略。如果异常的类型和except之后的名称相符,那么对应的except子句将被执行。最后执行try语句之后的代码。
  • 如果一个异常没有与任何的except匹配,那么这个异常将会传递给上层的try中。
    一个try语句可能包含多个except子句,分别来处理不同的特定的异常。最多只有一个分支会被执行。
    处理程序将只针对对应的try子句中的异常进行处理,而不是其他的try的处理程序中的异常。
    一个except子句可以同时处理多个异常,这些异常将被放在一个括号里成为一个元组。 如:
except (RuntimeError, TypeError, NameError): 
    pass

最后一个except子句可以忽略异常的名称,它将被当作通配符使用。你可以使用这种方法打印一个错误信息,然后再次把异常抛出。

import sys
try:
  f = open('myfile.txt')
  s = f.readline()
  i = int(s.strip())
except OSError as err:
  print("OS error: {0}".format(err))
except ValueError:
  print("Could not convert data to an integer.")
except:
  print("Unexpected error:", sys.exc_info()[0])
  raise

try except 语句还有一个可选的else子句,如果使用这个子句,那么必须放在所有的except子句之后。这个子句将在try子句没有发生任何异常的时候执行。例如:

for arg in sys.argv[1:]:
   try:
       f = open(arg, 'r')
   except IOError:
       print('cannot open', arg)
   else:
       print(arg, 'has', len(f.readlines()), 'lines')
       f.close()

使用 else 子句比把所有的语句都放在 try 子句里面要好,这样可以避免一些意想不到的、而except又没有捕获的异常。

异常处理并不仅仅处理那些直接发生在try子句中的异常,而且还能处理子句中调用的函数(甚至间接调用的函数)里抛出的异常。例如:

>>>def this_fails():
        x = 1/0
   
>>> try:
        this_fails()
    except ZeroDivisionError as err:
        print('Handling run-time error:', err)
   
Handling run-time error: int division or modulo by zero
5. 抛出异常

Python 使用 raise 语句抛出一个指定的异常。例如:

>>>raise NameError('HiThere')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: HiThere

raise 唯一的一个参数指定了要被抛出的异常。它必须是一个异常的实例或者是异常的类(也就是 Exception 的子类)。

如果你只想知道这是否抛出了一个异常,并不想去处理它,那么一个简单的 raise 语句就可以再次把它抛出。

>>>try:
        raise NameError('HiThere')
    except NameError:
        print('An exception flew by!')
        raise
   
An exception flew by!
Traceback (most recent call last):
  File "<stdin>", line 2, in ?
NameError: HiThere
  1. 用户自定义异常
    你可以通过创建一个新的异常类来拥有自己的异常。异常类继承自 Exception 类,可以直接继承,或者间接继承,例如:
>>>try:
...     raise KeyboardInterrupt
... finally:
...     print('Goodbye, world!')
... 
Goodbye, world!
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
KeyboardInterrupt

**不提供参数 **
捕获异常后,如果要重新引发它(即继续向上传播),可调用raise且提供任何参数(也可显示的提供捕获到的异常)
**捕获对象(**在捕获多个异常时,可以采用多个except子句,也可以向except提供一个参数即一个元组)

try:
    x=int(input('Enter the first number:'))
    y=int(input('Enter the second number:'))
    print(x/y)
except ZeroDivisionError:
    print("The second number cann't be zero!")
except TypeError:
    print ("That wasn't a number, was it?")
try:
    x=int(input('Enter the first number:'))
    y=int(input('Enter the second number:'))
    print(x/y)
except (ZeroDivisionError, TypeError) as e:
    print(e)

**当不顶用异常类型时,可以将所有错误都归为一起,打印相同的内容(这个的坏处是无法知道异常的类型,只是知道程序出错了): **

try:
    x=int(input('Enter the first number:'))
    y=int(input('Enter the second number:'))
    print(x/y)
except:
    print('Something wrong happened...')

**try/except也可以使用旧式字符串异常或引发并非从Exception派生而来的异常,同时,try/except语句也可以像条件循环一样,添加else语句 **

while True:
    try:
        x=int(input('Enter the first number:'))
        y=int(input('Enter the second number:'))
        value x/y
        print ('x/y is', value)
   except Exception as e:
       print('Invalid input:' e)
       print('please try again')
   else:
       break

** try-finally语句 **
finally语句可用于发生异常时执行清理工作,这个子句与try子句配套。

try:
    f=open('我是一个不存在的文档.txt')
print(f.read())
    sum=1+'1'
except:
    print('出错啦')
finally:
    f.close()

这个代码块表示try语句中没有出现任何运行错误,会跳过except语句执行finally语句块的内容。如果出现异常,则会执行except语句块的内容再执行finally语句块的内容。finally语句块中的内容是一定会被执行的。
**简洁的with语句 **
对文件操作使用with语句,可以大大减少代码量,而且不用担心出现文件打开忘记关闭的问题(with会自动帮你关闭文件)。

try:
    f=open('data.txt', 'w')
    for each_line in f:
        print(each_line)
except OSError as reason:
    print('出错了:'+str(reason))
finally:
    f.close()

使用with语句可以改成这样:

try:
    with open('data.txt', 'w')as f:
        for each_line in f:
            print(each_line)
except OSError as reason:
    print('出错了:'+str(reason))
  1. raise语句:要引发异常,可使用raise语句,并将一个类(必须是Exception的子类)或实例作为参数。将类作为参数时,将自动创建一个实例。
1. raise Exception
Traceback (most recent call last):
    File"<stdin>", line1, in?
Exception
这个例子里引发的是通用异常,没有指出出现了什么错误
2. raise Exception('hyperdrive overload')
Traceback (most recent call last):
    File"<stdin>", line1, in?
Exception:hyperdrive overload
这个例子中添加了错误信息hyperdrive overload
三、异常和函数   
异常与函数有天然的联系。如果不处理函数中引发的异常,它将向上传播到调用函数的地方。如果那里也未得到处理,那异常将会继续传播,直到到达主程序(全局作用域)。若主程序也没有处理,程序将终止并显示栈跟踪小心。  

猜你喜欢

转载自blog.csdn.net/ihblxh/article/details/84238157