python不是字母就异常_python 异常处理 - 刘江的python教程

异常处理

阅读: 13442

评论:2

在程序运行过程中,总会遇到各种各样的问题和错误。有些错误是我们编写代码时自己造成的,比如语法错误、调用错误,甚至逻辑错误。还有一些错误,则是不可预料的错误,但是完全有可能发生的,比如文件不存在、磁盘空间不足、网络堵塞、系统错误等等。这些导致程序在运行过程中出现异常中断和退出的错误,我们统称为异常。大多数的异常都不会被程序处理,而是以错误信息的形式展现出来。

异常有很多种类型,Python内置了几十种常见的异常,就在builtins模块内,无需特别导入,直接就可使用。需要注意的是,所有的异常都是异常类,首字母是大写的!

在发生异常的时候,Python会打印出异常信息,信息的前面部分显示了异常发生的上下文环境,并以调用栈的形式显示具体信息。异常类型作为信息的一部分也会被打印出来,例如ZeroDivisionError,NameError 和 TypeError。

>>> 10 * (1/0)

Traceback (most recent call last):

File "", line 1, in ?

ZeroDivisionError: division by zero

>>> 4 + spam*3

Traceback (most recent call last):

File "", line 1, in ?

NameError: name 'spam' is not defined

>>> '2' + 2

Traceback (most recent call last):

File "", line 1, in ?

TypeError: Can't convert 'int' object to str implicitly

为了保证程序的正常运行,提高程序健壮性和可用性。我们应当尽量考虑全面,将可能出现的异常进行处理,而不是留在那里,任由其发生。

Python内置了一套try...except...finally(else)...的异常处理机制,来帮助我们进行异常处理。其基本语法是:

try:

pass

except Exception as ex:

pass

注:在Python3中,原Python2的except Exception , ex的别名方法已经不能使用,逗号被认为是两种异常的分隔符,而不是取别名。

Python的异常机制具有嵌套处理的能力,比如下面的函数f3()调用f2(),f2()调用f1(),虽然是在f1()出错了,但只需要在f3()进行异常捕获,不需要每一层都捕获异常。

def f1():

return 10/0

def f2():

f1()

def f3():

f2()

f3()

------------------------

Traceback (most recent call last):

File "F:/Python/pycharm/201705/1.py", line 10, in

f3()

File "F:/Python/pycharm/201705/1.py", line 8, in f3

f2()

File "F:/Python/pycharm/201705/1.py", line 5, in f2

f1()

File "F:/Python/pycharm/201705/1.py", line 2, in f1

return 10/0

ZeroDivisionError: division by zero

仅仅需要在调用f3()函数的时候捕获异常:

def f1():

return 10/0

def f2():

f1()

def f3():

f2()

try:

f3()

except ZeroDivisionError as e:

print(e)

try…except…语句处理异常的工作机制如下:

首先,执行try子句(在关键字try和关键字except之间的语句)

如果没有异常发生,忽略except子句,try子句执行后结束。

如果在执行try子句的过程中发生了异常,那么try子句余下的部分将被忽略。如果异常的类型和 except 之后的名称相符,那么对应的except子句将被执行。

try:

print("发生异常之前的语句正常执行")

print(1/0)

print("发生异常之后的语句不会被执行")

except ZeroDivisionError as e:

print(e)

#--------------

结果:

发生异常之前的语句正常执行

division by zero

如果程序发生的异常不在你的捕获列表中,那么依然会抛出别的异常。

# 未捕获到异常,程序直接报错

s1 = 'hello'

try:

int(s1)

except IndexError as ex: # 本例为非法值异常,而你只捕获索引异常

print(ex)

# -----------

Traceback (most recent call last):

File "F:/Python/pycharm/201705/1.py", line 5, in

int(s1)

ValueError: invalid literal for int() with base 10: 'hello'

如果一个异常没有与任何的except匹配,那么这个异常将会传递给上层的try中。也就是前面说的嵌套处理能力。直到程序最顶端如果还没有被捕获,那么将弹出异常。

try:

try:

print("发生异常之前的语句正常执行")

print(1/0)

print("发生异常之后的语句不会被执行")

except ValueError as e:

print(e)

except ZeroDivisionError as e:

print("里层没有抓好,只能辛苦我外层了")

可能包含多个except子句,分别来处理不同的特定的异常。但最多只有一个分支会被执行。所以except子句有排序先后问题,进了一条巷子就不会进别的巷子。

try:

print("发生异常之前的语句正常执行")

print(1/0)

print("发生异常之后的语句不会被执行")

except NameError as e:

print(e)

except ZeroDivisionError as e:

print("我是第一个抓取到除零异常的")

except (ValueError,ZeroDivisionError) as e:

print("我是备胎")

#------------

发生异常之前的语句正常执行

我是第一个抓取到除零异常的

处理程序将只针对对应的try子句中的异常进行处理,不会处理其他try语句中的异常。

一个except子句可以同时处理多个异常,这些异常将被放在一个括号里成为一个元组。

except (RuntimeError, TypeError, NameError):

pass

最后一个except子句可以忽略异常的名称,它将被当作通配符使用,也就是说匹配所有异常。

except:

print("Unexpected error:", sys.exc_info()[0])

看一个综合性的例子:

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

通用异常:Exception

在Python的异常中,有一个通用异常:Exception,它可以捕获任意异常。

s1 = 'hello'

try:

int(s1)

except Exception as e:

print('错误')

那么既然有这个什么都能管的异常,其他诸如OSError、ValueError的异常是不是就可以不需要了?当然不是!很多时候程序只会弹出那么几个异常,没有必要针对所有的异常进行捕获,那样的效率会很低。另外,根据不同的异常种类,制定不同的处理措施,用于准确判断错误类型,存储错误日志,都是非常有必要甚至强制的。

finally和else子句

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()

同样的,还有一个可选的finally子句。无论try执行情况和except异常触发情况,finally子句都会被执行!

try:

print('try...')

r = 10 / int('a')

print('result:', r)

except ValueError as e:

print('ValueError:', e)

finally:

print('finally...')

print('END')

那么,当else和finally同时存在时:

try:

pass

except:

pass

else:

print("else")

finally:

print("finally")

运行结果:

else

finally

如果有异常发生:

try:

1/0

except:

pass

else:

print("else")

finally:

print("finally")

运行结果:

finally

主动抛出异常:raise

很多时候,我们需要主动抛出一个异常。Python内置了一个关键字raise,可以主动触发异常。

>>> raise

Traceback (most recent call last):

File "", line 1, in

raise

RuntimeError: No active exception to reraise

>>> raise NameError("kkk")

Traceback (most recent call last):

File "", line 1, in

raise NameError("kkk")

NameError: kkk

raise唯一的一个参数指定了要被抛出的异常的实例,如果什么参数都不给,那么会默认抛出当前异常。

可能有同学会问,为什么要自己主动抛出异常?不嫌多事么?因为有的时候,你需要记录错误信息,然后将异常继续往上层传递,让上层去处理异常,如下:

try:

1/0

except ZeroDivisionError as ex:

print("记录异常日志: ", ex)

print("但是我自己无法处理,只能继续抛出,看看上层能否处理(甩锅)")

raise

有时候,你需要主动弹出异常,作为警告或特殊处理:

sex = int(input("Please input a number: "))

try:

if sex == 1:

print("这是个男人!")

elif sex == 0:

print("这是个女人!")

else:

print("好像有什么不符合常理的事情发生了!!")

raise ValueError("非法的输入")

except ValueError:

print("这是个人妖!")

更多的时候,你需要使用raise抛出你自定义的异常,如下面所述!

自定义异常

Python内置了很多的异常类,并且这些类都是从BaseException类派生的。

下面是一些常见异常类,请把它们记下来!这样你在见到大多数异常的时候都能快速准确的判断异常类型。

异常名

解释

AttributeError

试图访问一个对象没有的属性

IOError

输入/输出异常

ImportError

无法引入模块或包;多是路径问题或名称错误

IndentationError

缩进错误

IndexError

下标索引错误

KeyError

试图访问不存在的键

KeyboardInterrupt

Ctrl+C被按下,键盘终止输入

NameError

使用未定义的变量

SyntaxError

语法错误

TypeError

传入对象的类型与要求的不符合

UnboundLocalError

试图访问一个还未被设置的局部变量

ValueError

传入一个调用者不期望的值,即使值的类型是正确的

OSError

操作系统执行错误

大多数情况下,上面的内置异常已经够用了,但是有时候你还是需要自定义一些异常。自定义异常应该继承Exception类,直接继承或者间接继承都可以,例如:

class MyExcept(Exception):

def __init__(self, msg):

self.message = msg

def __str__(self):

return self.message

try:

raise MyExcept('我的异常!')

except MyExcept as ex:

print(ex)

异常的名字都以Error结尾,我们在为自定义异常命名的时候也需要遵守这一规范,就跟标准的异常命名一样。

评论总数: 2

没看明白,那个机制是要我们自己写代码时一起写进去吗?还是啥意思?

By

虎啸云飘舞   On

2018年11月25日 22:37

回复

一遍又一遍的占

江哥,希望能加你的qq

By

海上有个树荫_hhh   On

2018年3月11日 23:36

回复

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值