zoukankan      html  css  js  c++  java
  • Python raise用法(超级详细,看了无师自通)

    能否手动抛出一个异常吗?

    答案是肯定的,Python允许程序自行引发异常,使用 raise 语句即可。

    异常是一种很“主观”的说法,以下雨为例,假设大家约好明天去爬山郊游,如果第二天下雨了,这种情况会打破既定计划,就属于一种异常;但对于正在期盼天降甘霖的农民而言,如果第二天下雨了,他们正好随雨追肥,这就完全正常。

    很多时候,系统是否要引发异常,可能需要根据应用的业务需求来决定,如果程序中的数据、执行与既定的业务需求不符,这就是一种异常。由于与业务需求不符而产生的异常,必须由程序员来决定引发,系统无法引发这种异常。

    如果需要在程序中自行引发异常,则应使用 raise 语句,该语句的基本语法格式为:

    raise [exceptionName [(reason)]]

    其中,用 [] 括起来的为可选参数,其作用是指定抛出的异常名称,以及异常信息的相关描述。如果可选参数全部省略,则 raise 会把当前错误原样抛出;如果仅省略 (reason),则在抛出异常时,将不附带任何的异常描述信息。

    也就是说,raise 语句有如下三种常用的用法:

    1. raise:单独一个 raise。该语句引发当前上下文中捕获的异常(比如在 except 块中),或默认引发 RuntimeError 异常。
    2. raise 异常类名称:raise 后带一个异常类名称。该语句引发指定异常类的默认实例。
    3. raise 异常类名称(描述信息):在引发指定异常的同时,附带异常的描述信息。

    上面三种用法最终都是要引发一个异常实例(即使指定的是异常类,实际上也是引发该类的默认实例),raise 语句每次只能引发一个异常实例。

    例如:

    >>> raise
    Traceback (most recent call last):
    File "<pyshell#1>", line 1, in
    raise
    RuntimeError: No active exception to reraise
    >>> raise ZeroDivisionError
    Traceback (most recent call last):
    File "<pyshell#0>", line 1, in
    raise ZeroDivisionError
    ZeroDivisionError
    >>> raise ZeroDivisionError("除数不能为零")
    Traceback (most recent call last):
    File "<pyshell#2>", line 1, in
    raise ZeroDivisionError("除数不能为零")
    ZeroDivisionError: 除数不能为零

    当然,我们手动让程序引发异常,很多时候并不是为了让其崩溃。事实上,raise 语句引发的异常通常用 try except(else finally)异常处理结构来捕获并进行处理。例如:

    try:    
    	a = input("输入一个数:")    
    	#判断用户输入的是否为数字    
    	if(not a.isdigit()):        
    	raise ValueError("a 必须是数字")
    except ValueError as e:    
    	print("引发异常:",repr(e))
    

    程序运行结果为:

    输入一个数:a
    引发异常: ValueError('a 必须是数字',)

    可以看到,当用户输入的不是数字时,程序会进入 if 判断语句,并执行 raise 引发 ValueError 异常。但由于其位于 try 块中,因为 raise 抛出的异常会被 try 捕获,并由 except 块进行处理。

    因此,虽然程序中使用了 raise 语句引发异常,但程序的执行是正常的,手动抛出的异常并不会导致程序崩溃。

    raise 不需要参数

    正如前面所看到的,在使用 raise 语句时可以不带参数,例如:

    try:    
    	a = input("输入一个数:")    
    	if(not a.isdigit()):        
    		raise ValueError("a 必须是数字")
    except ValueError as e:    
    	print("引发异常:",repr(e))    
    	raise
    

    程序执行结果为:

    输入一个数:a
    引发异常: ValueError('a 必须是数字',)
    Traceback (most recent call last):
    File "D:python3.61.py", line 4, in
    raise ValueError("a 必须是数字")
    ValueError: a 必须是数字

    这里重点关注位于 except 块中的 raise,由于在其之前我们已经手动引发了 ValueError 异常,因此这里当再使用 raise 语句时,它会再次引发一次。

    当在没有引发过异常的程序使用无参的 raise 语句时,它默认引发的是 RuntimeError 异常。例如:

    try:    
    	a = input("输入一个数:")    
    	if(not a.isdigit()):        
    		raise 
    except RuntimeError as e:    
    	print("引发异常:",repr(e))
    

    程序执行结果为:

    输入一个数:a
    引发异常: RuntimeError('No active exception to reraise',)

    except 和 raise 同时使用

    在实际应用中对异常可能需要更复杂的处理方式。当一个异常出现时,单靠某个方法无法完全处理该异常,必须由几个方法协作才可完全处理该异常。也就是说,在异常出现的当前方法中,程序只对异常进行部分处理,还有些处理需要在该方法的调用者中才能完成,所以应该再次引发异常,让该方法的调用者也能捕获到异常。

    为了实现这种通过多个方法协作处理同一个异常的情形,可以在 except 块中结合 raise 语句来完成。如下程序示范了except 和 raise 同时使用的方法:

    class AuctionException(Exception):
        pass
    
    class AuctionTest:
        def __init__(self, init_price):
            self.init_price = init_price
    
        def bid(self, bid_price):
            d = 0.0
            try:
                d = float(bid_price)
            except Exception as e:  # 此处只是简单地打印异常信息
                print("转换出异常:", e)  # 再次引发自定义异常
                raise AuctionException("竞拍价必须是数值,不能包含其他字符!")
                raise AuctionException(e)
            if self.init_price > d:
                raise AuctionException("竞拍价比起拍价低,不允许竞拍!")
    
            initPrice = d
    def main():
        at = AuctionTest(20.4)
        try:
            at.bid("df")
        except AuctionException as ae:  # 再次捕获到bid()方法中的异常,并对该异常进行处理
            print('main函数捕捉的异常:', ae)
    main()
    
    

    上面程序中 9~13 行代码对应的 except 块捕获到异常后,系统打印了该异常的字符串信息,接着引发一个 AuctionException 异常,通知该方法的调用者再次处理该 AuctionException 异常。所以程序中的 main() 函数,也就是 bid() 方法的调用者还可以再次捕获 AuctionException 异常,井将该异常的详细描述信息打印出来。

    这种 except 和 raise 结合使用的情况在实际应用中非常常用。实际应用对异常的处理通常分成两个部分:

    1. 应用后台需要通过日志来记录异常发生的详细情况;
    2. 应用还需要根据异常向应用使用者传达某种提示;

    在这种情形下,所有异常都需要两个方法共同完成,也就必须将 except 和 raise 结合使用。

    如果程序需要将原始异常的详细信息直接传播出去,Python 也允许用自定义异常对原始异常进行包装,只要将上面 ① 号代码改为如下形式即可:

    raise AuctionException(e)

    上面就是把原始异常 e 包装成了 AuctionException 异常,这种方式也被称为异常包装或异常转译。

    自定义异常类

    很多时候,程序可选择引发自定义异常,因为异常的类名通常也包含了该异常的有用信息。所以在引发异常时,应该选择合适的异常类,从而可以明确地描述该异常情况。在这种情形下,应用程序常常需要引发自定义异常。

    用户自定义异常都应该继承 Exception 基类或 Exception 的子类,在自定义异常类时基本不需要书写更多的代码,只要指定自定义异常类的父类即可。

    下面程序创建了一个自定义异常类:

    class AuctionException(Exception):
    pass

    上面程序创建了 AuctionException 异常类,该异常类不需要类体定义,因此使用 pass 语句作为占位符即可。

    在大部分情况下,创建自定义异常类都可采用与上面程序相似的代码来完成,只需改变 AuctionException 异常的类名即可,让该异常的类名可以准确地描述该异常。

    原文:http://c.biancheng.net/view/2360.html

  • 相关阅读:
    VScode 修改中文字体
    missing KW_END at ')' near '<EOF>'
    SQL inner join, join, left join, right join, full outer join
    SQL字符替换函数translater, replace
    SQL COOKBOOK SQL经典实例代码 笔记第一章代码
    sqlcook sql经典实例 emp dept 创建语句
    dateutil 2.5.0 is the minimum required version python
    安装postgresql后找不到服务 postgresql service
    Postgres psql: 致命错误: 角色 "postgres" 不存在
    【西北师大-2108Java】第十六次作业成绩汇总
  • 原文地址:https://www.cnblogs.com/remixnameless/p/13375086.html
Copyright © 2011-2022 走看看