zoukankan      html  css  js  c++  java
  • Python学习7——异常

    编写程序时,通常能够区分正常和异常情况。为了处理这些异常,可在每个可能出现异常的地方都使用上条件语句,但这样大大降低了程序的可读性,那么怎么解决哪?Python提供强大的替代解决方案——异常处理机制

    1、异常是什么?

    Python使用异常对象来表示异常状态,并在遇到错误时引发异常,异常对象未被处理(或捕获)时,程序将终止并显示一条错误消息。

    >>> 1/0
    Traceback (most recent call last):
      File "<pyshell#214>", line 1, in <module>
        1/0
    ZeroDivisionError: division by zero

    2、让事情沿着指定的轨道出错

    2.1 raise语句

    要引发异常,可使用raise语句,并将一个类(必须是Exception的子类)或实例作为参数。将类作为参数时,将自动创建一个实例。

    >>> raise Exception
    Traceback (most recent call last):
      File "<pyshell#0>", line 1, in <module>
        raise Exception
    Exception
    #上方引发的是通用异常,没有指出出现了什么错误
    >>> raise Exception('hyperdrive overload')
    Traceback (most recent call last):
      File "<pyshell#1>", line 1, in <module>
        raise Exception('hyperdrive overload')
    Exception: hyperdrive overload
    #上方示例添加了错误消息hyperdrive overload

    下方给出一些常用到的内置的异常类

    类名 描述
    Exception 几乎所有的异常类都是从它派生而来的
    AttributeError 引用属性或者给它赋值失败时引发
    OSError 操作系统不能执行指定任务时(如打开文件)引发,有多个子类
    IndexError 使用序列中不存在的索引时引发,为LookuoError的子类
    keyError 使用映射中不存在的键时引发,为LookupError的子类
    NameError 找不到名称(变量)时引发
    syntaxError 代码不正确时引发
    TypeError 将内置操作或函数用于类型不正确的对象时引发
    ValueError 将内置操作或者函数用于这样的对象时引发:其类型正确担包含的值不合适
    ZeroDivisionError 在除法或者求模运算中的第二个参数为零时引发

    2.2自定义的异常类

    虽然内置异常类的范围足够广泛,但有的时候你可能想要自己创建异常类满足自己的代码需要。

    那么如何创建自定义异常类哪?

    就像创建其他类一样,不过必须直接或间接的继承Exception

    >>>class someException(Exception):
        pass
    #当然,如果你乐意,也可以在自定义异常类中添加方法

    3、捕获异常

    try:
        x=int(input("Please input x:"))
        y=int(input("Please input y:"))
        print(x/y)
    except ZeroDivisionError:
            print("The second numbercna't be zero!")
    #一定要注意代码的缩进问题
    
    Please input x:1
    Please input y:0
    The second numbercna't be zero!

    3.1不用提供参数

    捕获异常之后,如果要重新引发它(即继续向上传播),可调用raise契额不提供任何参数(也可显式地提供捕获到的异常,参见3.4).

    这很有用,我们下面看一个例子:

    >>> class MuffledCalculator:
        mufflad=False
        def calc(self,expr):
            try:
                return eval(expr)
            except ZeroDivisionError:
                if self.mufflad:
                    print("Division by zero is illegal")
                else:
                    raise
    #发生除零行为时,如果启用了“抑制”功能,方法cacl将(隐式地)返回None。换而言之,如果启用了“抑制”功能,就不应依赖返回值。
            
    >>> calculator=MuffledCalculator()
    >>> calculator.calc('10/2')
    5.0
    >>> calculator.calc('10/0')#关闭了抑制功能
    Traceback (most recent call last):
      File "<pyshell#20>", line 1, in <module>
        calculator.calc('10/0')
      File "<pyshell#17>", line 5, in calc
        return eval(expr)
      File "<string>", line 1, in <module>
    ZeroDivisionError: division by zero
    
    >>> calculator.mufflad=True
    >>> calculator.calc('10/0')
    Division by zero is illegal

    如你所见,关闭抑制功能时,捕获了异常 ZeroDivisionError 但继续向上传播它。

    如果无法处理异常,在Except子句中使用不带参数的raise通常是不错的选择,但有时你可能想引发别的异常。在这种情况下,导致进入except子句的异常将被作为异常上下文存储起来,并出现在最终的错误消息中,如下所示:

    >>> try:
        1/0
    except ZeroDivisionError:
        raise ValueError
    
    Traceback (most recent call last):
      File "<pyshell#27>", line 2, in <module>
        1/0
    ZeroDivisionError: division by zero
    #在处理上述异常时引发了另一个异常
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "<pyshell#27>", line 4, in <module>
        raise ValueError
    ValueError

    你可以使用raise...from...语句来提供自己的异常上下文,也可以使用None来禁用上下文。

    >>> try:
        1/0
    except ZeroDivisionError:
        raise ValueError from None
    
    Traceback (most recent call last):
      File "<pyshell#32>", line 4, in <module>
        raise ValueError from None
    ValueError
    >>> 

    3.2多个except语句

    '
    >>> try:
        x=int(input("Please input x:"))
        y=int(input("Please input y:"))
        print(x/y)
    except ZeroDivisionError:
            print("The second numbercna't be zero!")
    except ValueError:
        print("That wasn't a number ,was it?")
    
    
    Please input x:1
    Please input y:0
    The second numbercna't be zero!
        
    Please input x:1
    Please input y:e
    That wasn't a number ,was it?
    >>> 

    3.3一箭多雕

    使用一个except 语句捕获多种异常,将异常类放在一个元组里。

    >>> try:
        x=int(input("Please input x:"))
        y=int(input("Please input y:"))
        print(x/y)
    except (ZeroDivisionError,ValueError):
            print("The numbers you input have bugs!")
    
            
    Please input x:1
    Please input y:0
         
    Please input x:3
    Please input y:d
    The numbers you input have bugs!

    3.4捕获对象

    要在except 语句中访问异常对象本身,可使用两个参数而不是一个参数(即便是你捕获多个异常时,也只是向except 提供了一个参数——一个元组。)需要让程序继续运行并记录错误(可能只是向用户显示)时,这很有用。下面这个示例,程序打印出现的异常并继续运行:

    >>> try:
        x=int(input("Please input x:"))
        y=int(input("Please input y:"))
        print(x/y)
    except (ZeroDivisionError,ValueError)as e:
            print(e)
    
            
    Please input x:1
    Please input y:0
    division by zero
    
    Please input x:1
    Please input y:r
    invalid literal for int() with base 10: 'r'

    3.5一网打尽

    即使程序处理了很多异常,但还是有漏网之鱼,例如上面程序什么都不输入就按回车键,仍然会出现一条错误信息,还有一些相关问题出现在什么地方的信息(栈跟踪),如下所示:

    Please input x:
    invalid literal for int() with base 10: ''

    这种异常没有被捕捉到,因为你根本就没有预料这种情况,那这么做那?

    >>> try:
        x=int(input("Please input x:"))
        y=int(input("Please input y:"))
        print(x/y)
    except :
            print("Something wrong happened...")

    这样更不行了,因为这不仅会隐藏你预料到的错误,更隐藏了你根本就没有想到的异常,你根本不知道哪出错了。。。

    在大多数情况下,更好的选择是使用except Exceeption as e 并对异常对象进行检查。

    3.6万事大吉时

    有时候,在没有出现异常时执行一个代码块很有用。为此可以给try except语句加一个else语句。

    同时,通过使用else 语句可以实现循环效果。

    最后还有finally子句,可用在发生异常时执行清理作用。这个子句是和try子句配套的。

    不管try子句中发生什么异常,都将执行finally子句

    >>> while True :
        try:
            x=int(input("entern the x:"))
            y=int(input("entern the y:"))
            value=x/y
            print("x/y is ",value)
        except Exception as e:
            print("Invalid input:",e)
            print("Please try again")
        else :
            break
        finally:
            print("Everything is OK!")
    
            
    entern the x:1
    entern the y:0
    Invalid input: division by zero
    Please try again
    Everything is OK!
    entern the x:1
    entern the y:2
    x/y is  0.5
    Everything is OK!

    4、异常和函数

    函数和异常有着天然的联系,如果不处理函数中引发的异常,它将会向上传播到调用函数的地方。如果在那里也未得到处理,将会继续传播,直至到达主程序(全局作用域),如果主程序也不处理,那么程序将会终止并显示栈跟踪信息。下面看一个例子:

    >>> def faulty():
        raise Exception('Something is wrong')
    
    >>> def ignore_exception():
        faulty()
    
        
    >>> def handle_exception():
        try:
            faulty()
        except:
            print("Exception handle")
    
            
    >>> ignore_exception()
    Traceback (most recent call last):
      File "<pyshell#39>", line 1, in <module>
        ignore_exception()
      File "<pyshell#30>", line 2, in ignore_exception
        faulty()
      File "<pyshell#27>", line 2, in faulty
        raise Exception('Something is wrong')
    Exception: Something is wrong
    >>> handle_exception()
    Exception handle

    如你所见,faulty引发的异常依次从faulty和ignore_exception向外传播,最终导致显示一条栈跟踪消息。

    调用handle_exception时,异常最终传播到handle_exception,并被这里的try/except语句处理。

    5、异常之禅

    异常处理并不复杂

    6、只是警告(此处会用到几个函数,你可能不理解,但后面会介绍新学的函数)

    有的时候,你只是想发出警告,指出情况偏离了正轨,可以使用warnings中的函数warn

    >>> from warnings import warn
    >>> warn("I've got a bad feeling about this.")
    
    Warning (from warnings module):
      File "__main__", line 1
    UserWarning: I've got a bad feeling about this.
    #警告只显示一次,如果再次运行最后一行代码,将什么都不显示

    如果其他代码在使用你的模块,可使用模块warnings 中的函数filterwarnings来抑制你发出的警告(或特定类型的警告),并指定要采取的措施,如"error"或者"ignore"

    >>> from warnings import filterwarnings
    >>> filterwarnings('ignore')
    >>> warn('Anyone out there')
    >>> filterwarnings("error")
    >>> warn("Something is very wrong!")
    Traceback (most recent call last):
      File "<pyshell#49>", line 1, in <module>
        warn("Something is very wrong!")
    UserWarning: Something is very wrong!

    如你所见,引发的异常为UserWarning,发出警告时,可指定将引发的异常(即警告类别),但必须是Warning的子类。

    如果将警告转换为错误,将使用你指定的异常。

    另外,还可根据异常来过滤掉特定类型的警告:

    >>> filterwarnings("error")
    >>> warn("This function is really old ......",DeprecationWarning)
    Traceback (most recent call last):
      File "<pyshell#59>", line 1, in <module>
        warn("This function is really old ......",DeprecationWarning)
    DeprecationWarning: This function is really old ......
    
    >>> filterwarnings("ignore",category=DeprecationWarning)
    >>> warn("Another deprecation warning.",DeprecationWarning)
    >>> warn("Something else.")
    Traceback (most recent call last):
      File "<pyshell#63>", line 1, in <module>
        warn("Something else.")
    UserWarning: Something else.
    >>> 

    小结:异常对象、引发异常、自定义的异常类、捕获异常、else子句、finally子句、异常和函数、警告

    本节新学习的函数
    函数 描述
    warnings.filterwarnings(action,category=Warning,...) 用于过滤警告
    warnings.warn(message,category=None) 用来发出警告
  • 相关阅读:
    37 什么时候使用内部临时表
    2 Hadoop集群安装部署准备
    36 为什么临时表可以重名
    maven内置属性
    ProGuard 最全混淆规则说明
    stylus的用法
    vscode 插件 配置
    关于overflow:hidden
    vue-devtools/安装vue-devtools
    对Java中使用两个大括号进行初始化的理解
  • 原文地址:https://www.cnblogs.com/jiameng991010/p/11240364.html
Copyright © 2011-2022 走看看