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) 用来发出警告
  • 相关阅读:
    Anagram
    HDU 1205 吃糖果(鸽巢原理)
    Codeforces 1243D 0-1 MST(补图的连通图数量)
    Codeforces 1243C Tile Painting(素数)
    Codeforces 1243B2 Character Swap (Hard Version)
    Codeforces 1243B1 Character Swap (Easy Version)
    Codeforces 1243A Maximum Square
    Codeforces 1272E Nearest Opposite Parity(BFS)
    Codeforces 1272D Remove One Element
    Codeforces 1272C Yet Another Broken Keyboard
  • 原文地址:https://www.cnblogs.com/jiameng991010/p/11240364.html
Copyright © 2011-2022 走看看