前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【十五】python之异常处理

【十五】python之异常处理

作者头像
菲宇
发布2019-06-13 10:49:26
1.1K0
发布2019-06-13 10:49:26
举报
文章被收录于专栏:菲宇菲宇

1、异常基础

在编程过程中为了增加友好性,在程序出现bug时一般不会将错误信息显示给用户,而是现实一个提示的页面,通俗来说就是不让用户看见大黄页!!!

1 2 3 4

try: pass except Exception,ex: pass

需求:将用户输入的两个数字相加

while True:
    num1 = raw_input('num1:')
    num2 = raw_input('num2:')
    try:
        num1 = int(num1)
        num2 = int(num2)
        result = num1 + num2
    except Exception, e:
        print '出现异常,信息如下:'
        print e

2、异常种类

python中的异常种类非常多,每个异常专门用于处理某一项异常!!!

AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
IOError 输入/输出异常;基本上是无法打开文件
ImportError 无法引入模块或包;基本上是路径问题或名称错误
IndentationError 语法错误(的子类) ;代码没有正确对齐
IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
KeyError 试图访问字典里不存在的键
KeyboardInterrupt Ctrl+C被按下
NameError 使用一个还未被赋予对象的变量
SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
TypeError 传入对象类型与要求的不符合
UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,
导致你以为正在访问它
ValueError 传入一个调用者不期望的值,即使值的类型是正确的
ArithmeticError
AssertionError
AttributeError
BaseException
BufferError
BytesWarning
DeprecationWarning
EnvironmentError
EOFError
Exception
FloatingPointError
FutureWarning
GeneratorExit
ImportError
ImportWarning
IndentationError
IndexError
IOError
KeyboardInterrupt
KeyError
LookupError
MemoryError
NameError
NotImplementedError
OSError
OverflowError
PendingDeprecationWarning
ReferenceError
RuntimeError
RuntimeWarning
StandardError
StopIteration
SyntaxError
SyntaxWarning
SystemError
SystemExit
TabError
TypeError
UnboundLocalError
UnicodeDecodeError
UnicodeEncodeError
UnicodeError
UnicodeTranslateError
UnicodeWarning
UserWarning
ValueError
Warning
ZeroDivisionError
dic = ["wupeiqi", 'alex']
try:
    dic[10]
except IndexError, e:
    print e
dic = {'k1':'v1'}
try:
    dic['k20']
except KeyError, e:
    print e
s1 = 'hello'
try:
    int(s1)
except ValueError, e:
    print e

对于上述实例,异常类只能用来处理指定的异常情况,如果非指定异常则无法处理。

1 2 3 4 5 6 7

# 未捕获到异常,程序直接报错 s1 = 'hello' try: int(s1) except IndexError,e: print e

所以,写程序时需要考虑到try代码块中可能出现的任意异常,可以这样写:

1 2 3 4 5 6 7 8 9

s1 = 'hello' try: int(s1) except IndexError,e: print e except KeyError,e: print e except ValueError,e: print e

万能异常 在python的异常中,有一个万能异常:Exception,他可以捕获任意异常,即:

1 2 3 4 5

s1 = 'hello' try: int(s1) except Exception,e: print e

接下来你可能要问了,既然有这个万能异常,其他异常是不是就可以忽略了!

答:当然不是,对于特殊处理或提醒的异常需要先定义,最后定义Exception来确保程序正常运行。

1 2 3 4 5 6 7 8 9

s1 = 'hello' try: int(s1) except KeyError,e: print '键错误' except IndexError,e: print '索引错误' except Exception, e: print '错误'

3、异常其他结构

1 2 3 4 5 6 7 8 9 10 11 12

try: # 主代码块 pass except KeyError,e: # 异常时,执行该块 pass else: # 主代码块执行完,执行该块 pass finally: # 无论异常与否,最终执行该块 pass

4、主动触发异常

1 2 3 4

try: raise Exception('错误了。。。') except Exception,e: print e

5、自定义异常

1 2 3 4 5 6 7 8 9 10 11 12

class WupeiqiException(Exception): def __init__(self, msg): self.message = msg def __str__(self): return self.message try: raise WupeiqiException('我的异常') except WupeiqiException,e: print e

6、断言

1 2 3 4 5

# assert 条件 assert 1 == 1 assert 1 == 2

导语:

在写代码的时候,经常会遇到异常。 python提供了两个功能来处理程序在运行中出现的异常和错误,可以使用该功能来调试python程序。

  1. 异常处理
  2. 断言(Assertions)

常用异常:

Exception  它可以捕获任意(绝大部分)异常。
AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
IOError 输入/输出异常;基本上是无法打开文件 
ImportError 无法引入模块或包;基本上是路径问题或名称错误 
IndentationError 语法错误(的子类),代码没有正确对齐 
IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
KeyError 试图访问字典里不存在的键 
KeyboardInterrupt Ctrl+C被按下 
NameError 使用一个还未被赋予对象的变量 
SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了) 
TypeError 传入对象类型与要求的不符合 
UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,导致你以为正在访问它 
ValueError 传入一个调用者不期望的值,即使值的类型是正确的 

python标准异常:

CrazyWing:Python自动化运维开发实战 十七、Python异常
CrazyWing:Python自动化运维开发实战 十七、Python异常
CrazyWing:Python自动化运维开发实战 十七、Python异常
CrazyWing:Python自动化运维开发实战 十七、Python异常
CrazyWing:Python自动化运维开发实战 十七、Python异常
CrazyWing:Python自动化运维开发实战 十七、Python异常

什么是异常?

异常即是一个事件,该事件会在程序执行过程中发生,影响程序的正常执行。 一般情况下,在Python无法正常处理程序时就会发生一个异常,异常是Python对象,表示一 个错误。

当Python脚本发生异常时我们需要捕获处理它,否则程序会终止执行。

#!/usr/bin/env python
try:
    print "%d" % (5 / 0)
except ZeroDivisionError:
    print "除数不能为零"
else:
    print "没有报错"
print "这是异常之后的代码"  #如果没有上面的异常处理,下面的代码是不会执行的
for i in range(10):
    print i

捕捉异常:

try/except语句用来检测try语句块中的错误,从而让except语句捕获异常信息并处理。 
如果你不想在异常发生时结束你的程序,只需在try里捕获它。 
语法: 
    try:
        <语句>                #运行别的代码
    except <名字>:
        <语句>                #如果在try部份引发了'name'异常
    except <名字>,<数据>:
        <语句>                #如果引发了'name'异常,获得附加的数据
    else:
        <语句>                #如果没有异常发生 

try的工作原理

当开始一个try语句后,python就在当前程序的上下文中作标记,这样当异常出现时就可 以回到这里,try子句先执行,接下来会发生什么依赖于执行时是否出现异常。

1. 如果当try后的语句执行时发生异常,python就跳回到try并执行第一个匹配该异常的
except子句,异常处理完毕,控制流就通过整个try语句(除非在处理异常时又引发新的异
常)。
2. 如果在try后的语句里发生了异常,却没有匹配的except子句,异常将被递交到上层的
try,或者到程序的最上层(这样将结束程序,并打印缺省的出错信息)。
3. 如果在try子句执行时没有发生异常,python将执行else语句后的语句(如果有else的
话),然后控制流通过整个try语句。

打开一个文件,在该文件中的写入内容,且并未发生异常:
    try:
        fh = open("testfile", "w")
        fh.write("这是一个测试文件,用于测试异常!!")
    except IOError:
        print "Error: 没有找到文件或读取文件失败"
    else:
        print "内容写入文件成功"
        fh.close()

结果:
    # python test.py 
        内容写入文件成功
    # cat testfile       # 查看写入的内容
        这是一个测试文件,用于测试异常!! 

例
打开一个文件,在该文件中的内容写入内容,但文件没有写入权限,发生了异常:
    try:
        fh = open("testfile", "w")    
        fh.write("这是一个测试文件,用于测试异常!!")
    except IOError:
        print "Error: 没有找到文件或读取文件失败"
    else:
        print "内容写入文件成功"
        fh.close() 

在执行代码前为了测试方便,先去掉 testfile 文件的写权限
再执行以上代码:
    $ python test.py                                #注意这里用的是普通用户
    Error: 没有找到文件或读取文件失败

使用except不带任何异常类型

你可以不带任何异常类型使用except,如下实例:

     try:
        正常的操作
        ......................
     except:
        发生异常,执行这块代码
        ......................
     else:
        如果没有异常执行这块代码  

以上方式try-except语句捕获所有发生的异常。但这不是一个很好的方式,我们不能通过该程序 识别出具体的异常信息。因为它捕获所有的异常。

使用except带多种异常类型

也可以使用相同的except语句来处理多个异常信息:

    try:
        正常的操作
        ................
    except(Exception1[, Exception2[,...ExceptionN]]]):
       发生以上多个异常中的一个,执行这块代码
       ......................
    else:
        如果没有异常执行这块代码

try-finally 语句

try-finally 语句无论是否发生异常都将执行最后的代码。

    try:
        <语句>
    finally:
        <语句>    #退出try时总会执行

例1:

    try:
        fh = open("testfile", "w")
        fh.write("这是一个测试文件,用于测试异常!!")
    finally:
        print "Error: 没有找到文件或读取文件失败"

例2:

import  time    
try:
    f=file("文件.py")    
    while True:
        line = f.read()
        if len(line)==0:
            break
        time.sleep(2)
        print line,
finally:
    f.close()
    print "hello"

例3:

    try:
        fh = open("testfile", "w")
        try:
            fh.write("这是一个测试文件,用于测试异常!!")
        finally:
            print "关闭文件"
            fh.close()
    except IOError:
        print "Error: 没有找到文件或读取文件失败"

异常的参数:

一个异常可以带上参数,可作为输出的异常信息参数。 你可以通过except语句来捕获异常的参数,如下所示:

     try:
        正常的操作
        ......................
     except ExceptionType, Argument:
        你可以在这输出 Argument 的值... 

变量接收的异常值通常包含在异常的语句中。在元组的表单中变量可以接收一个或者多个 值。 元组通常包含错误字符串,错误数字,错误位置。

例 以下为单个异常的实例:

    #!/usr/bin/python
    def temp_convert(var):
        try:
            return int(var)
        except ValueError, Argument:
            print "参数没  有包含数字\n", Argument

    # 调用函数
    temp_convert("xyz")

以上程序执行结果如下:
    $ python test.py 
        参数没有包含数字
        invalid literal for int() with base 10: 'xyz'

触发异常

可以使用raise语句自己触发异常 raise语法格式:

raise [Exception [, args [, traceback]]]

语句中Exception是异常的类型(例如,NameError)参数是一个异常参数值。该参数是可 选的,如果不提供,异常的参数是"None"。 最后一个参数是可选的(在实践中很少使用),如果存在,是跟踪异常对象。

例 一个异常可以是一个字符串,类或对象。 Python的内核提供的异常,大多数都是实例化的 类,这是一个类的实例的参数。 定义一个异常:

def functionName( level ):
    if level < 1:
        raise Exception("Invalid level!", level)
        # 触发异常后,后面的代码就不会再执行

注意:为了能够捕获异常,"except"语句必须有用相同的异常来抛出类对象或者字符串。 例如我们捕获以上异常,"except"语句如下:

    try:
        正常逻辑
    except "Invalid level!":
        触发自定义异常    
    else:
        其余代码

    #!/usr/bin/python
    def mye( level ):
        if level < 1:
            raise Exception("Invalid level!", level)
            # 触发异常后,后面的代码就不会再执行
    try:
        mye(0)                # 触发异常
    except "Invalid level!":
        print 1
    else:
        print 2

输出结果:
    $ python test.py 
    Traceback (most recent call last):
      File "test.py", line 11, in <module>
        mye(0)
      File "test.py", line 7, in mye
        raise Exception("Invalid level!", level)
    Exception: ('Invalid level!', 0)

用户自定义异常:

通过创建一个新的异常类,程序可以命名它们自己的异常。异常应该是典型的继承自 Exception类,通过直接或间接的方式。 以下为与RuntimeError相关的实例,实例中创建了一个类,基类为RuntimeError,用于在 异常触发时输出更多的信息。 在try语句块中,用户自定义的异常后执行except块语句,变量 e 是用于创建Networkerror 类的实例。

    class Networkerror(RuntimeError):
        def __init__(self, arg):
            self.args = arg
    在你定义以上类后,你可以触发该异常,如下所示:
    try:
        raise Networkerror("Bad hostname")
    except Networkerror,e:
        print e.args

万能异常

在python的异常中,有一个万能异常:Exception,它可以捕获任意异常。 例:

    #cat  aa.py
    s1 = 'hello'
    try:
        int(s1)
    except Exception,e:
        print e

执行结果:  
    #python aa.py 
    invalid literal for int() with base 10: 'hello'

既然有这个万能异常,其他异常是不是就可以忽略了?当然不是,对于特殊处理或提醒的异常需要先定义,最后定义Exception来确保程序正常运行。 例:

   s1 = 'hello'
    try:
        int(s1)
    except KeyError,e:
        print '键错误'
    except IndexError,e:
        print '索引错误'
    except Exception, e:
        print '错误'    

======================================

assert断言

使用assert断言是学习python一个非常好的习惯,assert断言句语格式及用法很简单。在没完善一个程序之前,我们不知道程序在哪里会出错,与其让它在运行最崩溃,不如在出现错误条件时就崩溃,这时候就需要assert断言的帮助。

assert断言的作用

assert断言是声明其布尔值必须为真的判定,如果发生异常就说明表达示为假。可以理解assert断言语句为raise-if-not,用来测试表示式,其返回值为假,就会触发异常。 assert断言语句的语法格式 assert expression

一些assert用法的语句供参考:

assert 1==1
assert 2+2==2*2
assert len(['my boy',12])<10
assert range(4)==[0,1,2,3]

如何为assert断言语句添加异常参数

assert的异常参数,其实就是在断言表达式后添加字符串信息,用来解释断言并更好的知道是哪里出了问题。格式如下:

assert expression [, arguments]

何时使用断言

Python的assert是用来检查一个条件,如果它为真,就不做任何事。如果它为假,则会抛出AssertError并且包含错误信息。例如:

py> x = 23
py> assert x > 0, "x is not zero or negative"
py> assert x%2 == 0, "x is not an even number"

Traceback (most recent call last):
File "", line 1, in ....
AssertionError: x is not an even number

很多人用assert作为一个很快和容易的方法来在参数错误的时候抛出异常。但这样做是错的,非常错误,有两个原因。首先AssertError不是在测试参数时应该抛出的错误。你不应该像这样写代码:

if
not isinstance(x, int):
raise AssertionError("not an int")

你应该抛出TypeError的错误,assert会抛出错误的异常。

但是,更危险的是,有一个关于assert的困扰:它可以被编译好然后从来不执行,如果你用 –O 或 –oo 选项运行Python,结果不保证assert表达式会运行到。当适当的使用assert时,这是未来,但是当assert不恰当的使用时,它会让代码用-O执行时出错。

那什么时候应该使用assert?没有特定的规则,断言应该用于:

    防御型的编程
    运行时检查程序逻辑
    检查约定
    程序常量
    检查文档

(在测试代码的时候使用断言也是可接受的,是一种很方便的单元测试方法,你接受这些测试在用-O标志运行时不会做任何事。我有时在代码里使用assert False来标记没有写完的代码分支,我希望这些代码运行失败。尽管抛出NotImplementedError可能会更好。)

关于断言的意见有很多,因为它能确保代码的正确性。如果你确定代码是正确的,那么就没有用断言的必要了,因为他们从来不会运行失败,你可以直接移除这些断言。如果你确定检查会失败,那么如果你不用断言,代码就会通过编译并忽略你的检查。

在以上两种情况下会很有意思,当你比较肯定代码但是不是绝对肯定时。可能你会错过一些非常古怪的情况。在这个情况下,额外的运行时检查能帮你确保任何错误都会尽早地被捕捉到。

另一个好的使用断言的方式是检查程序的不变量。一个不变量是一些你需要依赖它为真的情况,除非一个bug导致它为假。如果有bug,最好能够尽早发现,所以我们为它进行一个测试,但是又不想减慢代码运行速度。所以就用断言,因为它能在开发时打开,在产品阶段关闭。

一个非变量的例子可能是,如果你的函数希望在它开始时有数据库的连接,并且承诺在它返回的时候仍然保持连接,这就是函数的不变量:

def
some_function(arg):

    assert
not  DB.closed()

    ...
# code goes here

    assert
not  DB.closed()

    return
result

断言本身就是很好的注释,胜过你直接写注释:

# when we reach here, we know that n > 2
你可以通过添加断言来确保它:
assert n > 2

断言也是一种防御型编程。你不是让你的代码防御现在的错误,而是防止在代码修改后引发的错误。理想情况下,单元测试可以完成这样的工作,可是需要面对的现实是,它们通常是没有完成的。人们可能在提交代码前会忘了运行测试代码。有一个内部检查是另一个阻挡错误的防线,尤其是那些不明显的错误,却导致了代码出问题并且返回错误的结果。

加入你有一些if…elif 的语句块,你知道在这之前一些需要有一些值:

# target is
expected to be one of x, y, or z, and nothing else.
if
target == x:
    run_x_code()
elif target == y:
    run_y_code()
else:
    run_z_code()

假设代码现在是完全正确的。但它会一直是正确的吗?依赖的修改,代码的修改。如果依赖修改成 target = w 会发生什么,会关系到run_w_code函数吗?如果我们改变了代码,但没有修改这里的代码,可能会导致错误的调用 run_z_code 函数并引发错误。用防御型的方法来写代码会很好,它能让代码运行正确,或者立马执行错误,即使你在未来对它进行了修改。

在代码开头的注释很好的一步,但是人们经常懒得读或者更新注释。一旦发生这种情况,注释会变得没用。但有了断言,我可以同时对代码块的假设书写文档,并且在它们违反的时候触发一个干净的错误

assert target in
(x, y, z)
if
target == x:
    run_x_code()
elif target == y:
    run_y_code()
else:
    assert target == z
    run_z_code()

这样,断言是一种防御型编程,同时也是一种文档。我想到一个更好的方案:

if
target == x:
    run_x_code()
elif target == y:
    run_y_code()
elif target == z:
    run_z_code()
else:
    # This can never happen. But just in
case  it does...
    raise RuntimeError("an unexpected error occurred")

按约定进行设计是断言的另一个好的用途。我们想象函数与调用者之间有个约定,比如下面的:

“如果你传给我一个非空字符串,我保证传会字符串的第一个字母并将其大写。”

如果约定被函数或调用这破坏,代码就会出问题。我们说函数有一些前置条件和后置条件,所以函数就会这么写:

def first_upper(astring):
    assert isinstance(astring, str) and len(astring) > 0
    result = astring[0].upper()
    assert isinstance(result, str) and len(result) == 1
    assert result == result.upper()
    return
result

按约定设计的目标是为了正确的编程,前置条件和后置条件是需要保持的。这是断言的典型应用场景,因为一旦我们发布了没有问题的代码到产品中,程序会是正确的,并且我们能安全的移除检查。

建议不要用断言的场景:

  1. 不要用它测试用户提供的数据
  2. 不要用断言来检查你觉得在你的程序的常规使用时会出错的地方。断言是用来检查非常罕见的问题。你的用户不应该看到任何断言错误,如果他们看到了,这是一个bug,修复它。
  3. 有的情况下,不用断言是因为它比精确的检查要短,它不应该是懒码农的偷懒方式。
  4. 不要用它来检查对公共库的输入参数,因为它不能控制调用者,所以不能保证调用者会不会打破双方的约定。
  5. 不要为你觉得可以恢复的错误用断言。换句话说,不用改在产品代码里捕捉到断言错误。
  6. 不要用太多断言以至于让代码很晦涩。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年12月24日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 导语:
  • 常用异常:
  • python标准异常:
  • 什么是异常?
  • 捕捉异常:
  • try的工作原理
  • 使用except不带任何异常类型
  • 使用except带多种异常类型
  • try-finally 语句
  • 异常的参数:
  • 触发异常
  • 用户自定义异常:
  • 万能异常
  • assert断言
    • assert断言的作用
      • 如何为assert断言语句添加异常参数
        • 何时使用断言
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档