Python异常处理机制

第一章、UML图与Python实现

UML类图

第二章、异常处理机制

一、概述

        在Python中,异常处理机制用于处理程序运行过程中出现的异常情况,以保证程序的稳定性和可靠性。异常是程序运行时的一种不正常的情况,可能导致程序终止或产生不可预料的结果。Python提供了一套强大的异常处理机制,使开发人员能够捕获、处理和抛出异常。

异常就是程序执行过程中出现了不正常的情况,Python是一个很完善的语言,提供了异常的处理方式,程序在执行过程中出现了不正常的情况,Python就会把该异常信息输出到控制台,供程序员参考。程序员看到异常信息之后,可以对程序进行修改,让程序更健壮

class ExceptionTest01(object):

    """
    ZeroDivisionError: division by zero
    """
    def test(self):
        # a = 10
        # b = 0
        # """
        # 实际上PVM再执行到此处的时候,会自动实例化异常对象,ZeroDivisionError("division by zero")
        # 并且PVM将异常对象抛出,输出到控制台了
        # """
        # result = a / b
        # # print(result)
        #
        # #执行到此处又会创建ZeroDivisionError异常对象
        # print(10 / 0)

        #程序员看到异常后,会对代码进行调试
        a = 10
        b = 0
        if b == 0:
            print("除数不能为0")
            return
        #程序执行到此处,说明b不为0
        print(a / b)


if __name__ == '__main__':
    ExceptionTest01().test()
    # help(ZeroDivisionError)

    print(ZeroDivisionError("898")) #898

    print(ZeroDivisionError.__mro__) #(<class 'ZeroDivisionError'>, <class 'ArithmeticError'>, <class 'Exception'>, <class 'BaseException'>, <class 'object'>)
    # print(ZeroDivisionError.mro()) #[<class 'ZeroDivisionError'>, <class 'ArithmeticError'>, <class 'Exception'>, <class 'BaseException'>, <class 'object'>]

当谈论 Python 中的异常处理时,可以从以下五个方面来理解:

1、异常类(Exception Class)

         异常类是用于表示不同类型错误和异常情况的类。Python 提供了许多内置的异常类,例如 ZeroDivisionErrorValueErrorFileNotFoundError 等。您也可以自定义自己的异常类,继承自内置的异常类或其他基类。通过使用异常类,您可以在发生错误时创建异常对象,以便在代码中引发和处理异常。

2、异常处理语句

        异常处理语句使用 tryexceptelsefinally 关键字来捕获和处理异常。try 语句块用于包含可能引发异常的代码,而 except 语句块用于指定捕获特定异常的处理操作。else 语句块在没有异常发生时执行,finally 语句块中的代码无论是否发生异常都会执行。异常处理语句允许您在程序中定义异常情况的处理逻辑,以确保代码可以优雅地处理问题而不会崩溃。

3、异常处理机制的工作流程

异常处理机制的工作流程如下:

  • 执行 try 块中的代码。
  • 如果在 try 块中引发异常,控制流将跳转到匹配的 except 块。
  • 如果没有匹配的 except 块,异常会向上级调用栈传播,直到找到合适的异常处理器或导致程序终止。
  • 如果没有异常发生,try 块中的代码执行完毕后,else 块中的代码将被执行。
  • 最终,无论是否引发异常,finally 块中的代码都会执行。

4、异常处理的方式

        在 Python 中,异常处理的方式包括捕获和处理异常、引发异常、创建自定义异常类以及使用异常处理器层级结构。您可以使用 tryexcept 来捕获和处理异常,使用 raise 来引发异常,使用 class 来创建自定义异常类。您还可以通过多个 except 块和继承来定义异常处理器的层级结构,以实现不同层次的异常处理逻辑。

5、异常处理的层级结构

        在异常处理中,可以根据异常类型的层级结构来捕获和处理异常。Python 中的异常类是按照继承关系进行组织的,这意味着子类异常会被父类异常的处理器捕获。因此,可以先捕获更具体的异常,然后再捕获更一般的异常。这样的层级结构允许您按照异常的特定性质逐层处理,以确保不同类型的异常可以得到适当的处理。

通过理解和运用这些方面,您可以在 Python 中有效地处理异常,提高代码的健壮性和可靠性。

二、异常的存在形式(类和对象的形式)

1、异常在Python中以类的形式存在,每一个异常类都可以创建异常对象

2、当程序运行不正常时,PVM就会就会根据异常类创建异常对象并抛出,若程序中有多个相同的异常,则PVM会根据该异常类分别实例出异常对象(虽然是同一个异常类,但是异常对象并不是同一个)并抛出

1、异常对应现实生活的关系

火灾(异常类)

  • 2008年8月8日,小明家着火了(异常对象)
  • 2008年8月9日,小刚家着火了(异常对象)
  • 2008年10月8日,小李家着火了(异常对象)

类是模版,对象是实际中存在的个体

钱包丢了(异常类)

  • 2009年1月2日 小明钱包丢了(异常对象)
  • 2009年1月10日 小方钱包丢了(异常对象)
class ExceptionTest02(object):

    def main(self):
        zerodivisionerror = ZeroDivisionError("除数不能为0")
        print("ZeroDivisionError:" , zerodivisionerror) #ZeroDivisionError: 除数不能为0

        arithmeticerror = ArithmeticError("计算错误")
        print("ArithmeticError:" ,arithmeticerror) #ArithmeticError: 计算错误


if __name__ == '__main__':
    e = ExceptionTest02()
    e.main()

三、异常类的继承结构

异常在Python中以类和对象的形式存在。那么异常的继承结构是怎样的?
我们可以使用UML图来描述一下继承结构。
画UML图有很多工具,例如: Rational Rose (收费的)、starum等...

1、UML图

  • UML是一种统一建模语言,一种图标式语言(画图的)
  • UML不是只有Python中使用。只要是面向对象的编程语言,都有UML。
  • 一般画UML图的都是软件架构师或者说是系统分析师。这些级别的人员使用的
  • 软件设计人员使用UML。
  • 在UML图中可以描述类和类之间的关系,程序执行的流程,对象的状态等
  • 盖大楼和软件开发一样,一个道理。
  • 盖楼之前,会先由建筑师画图纸。图纸上一个一个符号都是标准符号。这个图纸画完,只要是搞建筑的都能看懂,因为这个图纸上标注的这些符号都是一种“标准的语言”。
  • 在Python软件开发当中,软件分析师/设计师负责设计类,Python软件开发人员必须要能看懂。

2、异常类继承结构图示

SystemExit,KeyboardInterrupt,GeneratorExit ,和Exception(异常)都统称为异常,不正常的现象

Object

Object下有BaseException (Python 异常类层次结构的基类,它是所有异常的基础)

BaseException 下有四个分支:

  • SystemExit # 解释器请求退出
  • KeyboardInterrupt 用户中断执行 ( 通常是输入 ^C)
  • GeneratorExit # 生成器(generator)发生异常来通知退出
  • Exception # 常规异常的基类

 3、所有异常都是发生在运行阶段

        Python 中的异常通常在程序的运行阶段(runtime)发生。这意味着异常是在程序执行过程中由于某种原因(例如错误的输入、无效的操作等)导致的错误或问题。当程序在运行时遇到无法处理的情况时,会引发异常。

        Python 的异常处理机制允许您捕获并处理在程序执行期间可能出现的异常。异常可以是由内置操作、函数调用、算术操作、文件操作等引起的。一旦异常被引发,程序的正常执行流程将中断,并且控制权将传递给异常处理器(通过 tryexcept 关键字指定)来处理异常情况。

4、Python只有运行时异常,没有编译时异常

Python 的异常处理机制允许您在程序的运行时捕获和处理各种异常,而不需要显式地声明可能的异常。这种设计使得 Python 更加灵活,同时要求开发者在编写代码时更加小心,以避免潜在的异常情况。

        与某些其他编程语言(如 Java)不同,Python 没有严格的编译时异常。在 Python 中,所有异常都是在程序的运行时发生的,称为运行时异常(runtime exceptions)或运行时错误

        在 Python 中,如果在程序执行过程中遇到无法处理的错误或问题,会引发异常。这些异常在程序的运行时被捕获并处理,以提供有关错误的信息并允许程序采取适当的行动。

        与 Java 不同,Python 不要求您在方法或函数签名中显式声明可能引发的异常(受检异常),而是允许所有异常都在运行时进行处理。这使得 Python 编码更加灵活,但也要求开发者在编写代码时更加小心,以确保适当地处理可能的异常情况

5、方法级别默认上抛Exception(运行时异常)

  • 所有方法在方法声明处都默认上抛运行时异常
  • 将异常上抛给方法的调用者,调用者中没有捕获处理,则调用者方法终止,继续将异常上抛给调用者的调用者,直到PVM,PVM终止程序运行

四、raise关键字(抛出异常对象)

        在 Python 中,raise 关键字用于手动引发异常。您可以使用 raise 关键字来创建并引发自定义的异常,或者重新引发已经捕获的异常。

raise 关键字允许您在需要的地方手动引发异常,以便在程序中处理各种异常情况。您可以引发内置异常,也可以创建自定义异常类,然后使用 raise 关键字引发这些异常。

raise比return影响还要大,

  • 当执行到return时,方法结束
  • 当执行到raise时,方法结束,如不手动捕获处理,异常上抛,程序终止

总结:只要程序执行到return或者raise方法必然结束

1、语法

raise [ExceptionClassName("Error message")]

        其中,ExceptionClassName 是异常类的名称,可以是 Python 内置的异常类(例如 ValueErrorTypeError 等),也可以是您自己定义的异常类。可以选择性地在括号中提供错误消息,以便提供有关异常的更多信息。

以下是一些 raise 关键字的用法示例:

2、直接抛出异常对象(在非异常处理语句中)

class ExceptionTest03(object):
    def divide(self,a,b):
        if b == 0:
            raise ZeroDivisionError("除数不能为0") #抛出异常对象
        print(a / b)
def main():
    e = ExceptionTest03()
    # e.divide(1,0) #ZeroDivisionError: 除数不能为0
    e.divide(4,2) #2.0

if __name__ == '__main__':
    main()

在这个例子中,在divide方法中主动抛出异常对象,并默认上抛给调用者,让调用者进行处理,如没有处理则报错,程序终止。

3、重新抛出已捕获的异常对象(仅在异常处理语句中)

try:
    x = 10 / 0
except ZeroDivisionError as e:
    print("Caught an exception:", e)
    raise  # 重新引发已捕获的异常

        在这个示例中,首先捕获了 ZeroDivisionError 异常,然后使用 raise 关键字重新引发了该异常,将其传递给上层的异常处理器。

4、从下往上查看异常追踪信息

Python中查看异常追踪信息是从下往上,正好和java中是相反的。

# 作   者:JZQ
# 开发时间:2023/8/15 8:47
"""
查看异常信息,快速定位问题
    异常信息追踪信息,从下往上看,一行一行看
    但需要注意的是,Python内置模块的代码就不用看了,问题还是在自己写的代码上
"""
class ExceptionTest09(object):
    @classmethod
    def main(cls):
        try:
            cls.n1()
        except:
            raise
        """
        Traceback (most recent call last):
        File "D:\pythonProject\Basic\exception\exception_test09.py", line 37, in <module>
        ExceptionTest09.main()
        File "D:\pythonProject\Basic\exception\exception_test09.py", line 12, in main
        cls.n1()
        File "D:\pythonProject\Basic\exception\exception_test09.py", line 34, in n1
        f = open("bbb","r")
        FileNotFoundError: [Errno 2] No such file or directory: 'bbb'
        
        因为34行出问题导致第12行出问题
        第12行出问题导致第37行出问题
        
        应该先查看第34行的代码,34行代码是错误的根源
        """
        print("以下代码不能执行,因为是使用raise将异常默认上抛到了PVM,PVM让程序终止")

    @classmethod
    def n1(cls):
        f = open("bbb","r")

if __name__ == '__main__':
    ExceptionTest09.main()

五、Python会自动实例化内置异常类对象

  • 无论是使用内置的异常类还是自定义的异常类,实例化异常对象都允许您在代码中捕获和处理错误情况,以确保程序可以优雅地处理异常而不会崩溃。
  • 输出异常对象时会自动调用类似于java中的toString方法,输出异常的信息

        在 Python 中,运行时会自动实例化内置的异常类来创建异常对象。异常类是 Python 的预定义类,用于表示不同类型的错误和异常情况。要实例化异常对象,只需创建异常类的实例并传递适当的参数(如果有的话)。

以下是实例化异常对象的示例:

try:
    # 一些可能引发异常的代码
    result = 10 / 0  # 除以零错误
except ZeroDivisionError as e:
    # 实例化 ZeroDivisionError 异常对象,并将其赋值给变量 e
    print("捕获到异常:", e) #捕获到异常: division by zero

在这个示例中,我们捕获了一个 ZeroDivisionError 异常,然后将其实例化并将其赋值给变量 e。 通过这个实例对象,您可以访问异常的信息,如错误消息、错误码等。

六、try   except  else  finally语句(异常处理语句)

1、语法结构

try:
    # 代码块,可能会引发异常的部分
    # 如果发生异常,会跳转到相应的 except 块
    # 如果没有异常,会执行 else 块
except ExceptionType1 as e1:
    # 异常处理代码块1,处理 ExceptionType1 类型的异常
except ExceptionType2 as e2:
    # 异常处理代码块2,处理 ExceptionType2 类型的异常
else:
    # 如果没有异常发生,会执行这里的代码块
finally:
    # 不管是否发生异常,都会执行这里的代码块

try块中包含可能会抛出异常的代码。当try块中的代码抛出异常时,会根据异常的类型进行匹配,如果匹配到对应的except块,则执行相应的处理代码。(如果没有匹配到合适的except块,异常将被传递到上一级调用者进行处理,或者如果没有上级调用者处理该异常,则程序将终止并输出异常信息    指的是运行时异常,因为方法默认上抛Exception,如果异常没有在except块中被捕获,则会传递到上一级调用者处理。)

2、执行流程

  1. 执行try块中的代码。
  2. 如果try块中的代码抛出了异常,会与except块中的异常类型进行匹配。
  3. 如果找到与抛出的异常类型匹配的except块,则执行该except块中的代码,并跳过其他except块。
  4. 如果没有找到与抛出的异常类型匹配的except块,则异常将被传递给上一级调用者。
  5. 如果try块中没有发生异常,则会跳过 except 块。
  6. 如果存在else块,且try块中没有发生异常,将会执行 else 块中的代码。这个块用于处理在 try 块中没有异常的情况,通常用于放置当没有异常时需要执行的代码逻辑。
  7. 如果存在finally块,不论是否有异常抛出,finally块中的代码都会被执行。这个块通常用于执行清理操作,例如释放资源或确保在程序退出之前必须执行的操作。
  8. 程序继续执行后续的代码(只有使用try except语句将异常处理后)。

可以理解为

1、try和else是一个线程,finally是另一个线程,当try线程中检测到异常还是有return语句,则会开启另一个线程来执行finally语句块,所以无论try中有异常还是有return语句,finally都会执行

2、catch是另一个线程,专门用于捕获异常

3、3线程同时运行,异步操作

3、try代码块(必选)

1、将有可能出现异常的代码放到try代码块中

2、try代码块中的语句出现异常,则try代码块中后续代码不会执行

try 语句块包含您希望尝试执行的代码,这些代码可能会引发异常。如果在 try 块中引发了异常,程序将跳转到相应的 except 块来处理异常。

try:
    print(10 / 0)
    print(123) #此行代码并不会执行
except ZeroDivisionError as e:
    print(e) #division by zero

4、except代码块(可选)

except 语句块用于捕获并处理在 try 块中引发的异常。您可以为不同类型的异常编写多个 except 块,以便根据异常类型执行不同的处理逻辑。如果没有异常发生,except 块将被跳过。

  • 可以有多个except块,每个块可以捕获不同类型的异常,并进行相应的处理(精确处理异常)。
  • except块中的异常类型必须与抛出的异常类型相匹配或是其父类。
  • except写多个的时候,从上到下捕获的异常必须是从小到大的顺序
  • 可以嵌套使用try-catch语句,内层的try-catch语句可以捕获并处理内部代码块中抛出的异常。

5、else代码块(可选)

是可选的关键字,用于指定当没有异常发生时要执行的代码块。如果没有任何异常被捕获,就会执行这个块中的代码。

else 语句块在没有异常发生时执行,它位于 try 块之后的所有 except 块之后。这可以用于在没有异常发生时执行一些额外的操作,例如在成功执行 try 块中的代码后执行清理操作。

5、finally代码块(可选)

finally 语句块无论是否发生异常,都会在 try 块中的代码执行完毕后执行。它通常用于确保资源的释放,例如关闭文件或释放网络连接,无论是否发生异常都要执行这些操作。

  • finally块是可选的,用于定义无论是否有异常抛出,都必须执行的代码。
  • 在finally代码块中的代码是最后执行的,并且一定会执行的,即使try代码块中的代码出现了异常,也会执行
  • finally代码块必须和try 一起出现,(没有catch也可以)不能单独编写
  • finally代码块使用的情况:
    • 通常在finally语句块中完成资源的释放/关闭
    • 因为finally中的代码比较有保障
    • 即使try语句块中的代码出现异常,finally中的代码也会正常执行

5.1、finally用法示例一:finally代码块关闭流

# 作   者:JZQ
# 开发时间:2023/8/12 8:09

"""
/**
 * 关于try catch 中的finally字句:
 *      1、在finally字句中的代码是最后执行的,并且一定会执行的,即使try语句块中的代码出现了异常
 *          finally子句必须和try一起出现,不能单独编写
 *      2、finally语句通常使用在哪些情况下尼?
 *          通常在finally语句块中完成资源的释放/关闭
 *          因为finally中的代码比较有保障
 *          即使try语句块中的代码出现异常,finally中的代码也会正常执行
 */
"""
class ExceptionTest10(object):
    def main(self):
        f = None
        try:
            #创建IO流对象
            f = open("test.txt","r",encoding="utf-8")
            print("流已开启")

            s:str = None
            #这里一定会出现 AttributeError
            s.lower()
            """
            流使用完需要关闭,因为流是占用资源的
            即使以上代码出现问题,流也必须要关闭
            放在这里流可能关闭不了
            """
            # f.close() #这行代码执行不了
            # print("流已关闭")
        except FileNotFoundError as e:
            print(e)
        except AttributeError as e:
            print("属性异常:",e)
        finally:
            print("finally代码块执行了")
            """
            放在这里关闭流 比较保险
            因为finally中的代码是一定会执行的
            即使try中出现了异常
            """
            if f is not None:
                f.close()
                print("流已关闭")
        print("异常捕获后执行!")


if __name__ == '__main__':
    e = ExceptionTest10()
    e.main()
    """
    执行结果:
    流已开启
    属性异常: 'NoneType' object has no attribute 'lower'
    finally代码块执行了
    流已关闭 
    异常捕获后执行!
    """

5.2、finally用法示例二:try块中有return语句,异常语句,sys.exit(),finally代码块一定会执行

1、没有catch,try和finally可以联合使用

2、理解try和finally线程的执行顺序

  • 先执行try线程
  • 在执行finally线程

3、finally比较特殊,无论try中有return语句还是有异常,都会先执行finally块,在执行try中的return或者异常语句

4、即使try块中有sys.exit()语句,退出PVM,finally块也会执行

# 作   者:JZQ
# 开发时间:2023/8/13 15:36

class ExceptionTest11(object):
    def main(self):

        """
        没有except,try和finally可以联合使用
        try不能单独使用

        以下代码的执行顺序:
            先执行try
            再执行finally
            最后执行return(return语句只要执行方法必然结束)

        """
        try:
            print("try...")
            # return
            raise AttributeError("123")

        finally:
            #finally中的语句会执行
            print("finally...")
        #若是try中有return或者可能输出异常的语句,则这里不能写语句
        #因为此处没有except来捕获异常,处理异常,一旦有异常的话,方法结束,程序停止
        #所以这个代码是无法执行的
        # print("这里不能写语句")

if __name__ == '__main__':
    e = ExceptionTest11()
    e.main()
    """
    try...
    finally...
    Traceback (most recent call last):
    File "D:\python\exception\exception_test11.py", line 32, in <module>
    e.main()
    File "D:\python\exception\exception_test11.py", line 20, in main
    raise AttributeError("123")
    AttributeError: 123
    """
# 作   者:JZQ
# 开发时间:2023/8/13 17:26
import sys

class ExceptTest12(object):
    def main(self):
        try:
            print("try...")
            #退出PVM
            sys.exit()

        finally:
            print("finally...")
        # print("这里可以写代码,但是JVM已经退出了,不会执行")

if __name__ == '__main__':
    e = ExceptTest12()
    e.main()
    """
    try...
    finally...
    """

5.3、finally面试题:try,catch,finally多线程和反编译

  • try中的return语句一定会在finally之后执行
  • try块中的return语句有闭包立即绑定特性
    • return的值是其变量名中最初声明的值,即使finally中对原始值修改,也会返回原始的值
# 作   者:JZQ
# 开发时间:2023/8/14 8:45

"""
finally面试题
"""
class ExceptionTest13(object):

    """
    Python语法规则(有一些规则是不能破坏的)
        方法体中的代码必须遵守自上而下的顺序依次执行
        return语句一旦执行,整个方法必须结束
    """
    @classmethod
    def m(cls):
        i = 100
        try:
            """
            这行代码出现在 i = 100 的下面,所以最终结果必须返回100
            return语句必须保证是最后执行的,一旦执行,这个方法结束
            """
            return i
        finally:
            # i += 1
            i = 200
    @classmethod
    def main(cls):
        print(cls.m()) #100

if __name__ == '__main__':
    ExceptionTest13.main()

5.4、finally面试题:类似于java中final,finally,finalize的区别

Python 中有与 Java 中的 finalfinallyfinalize 相关的概念,但在用法和含义上有些差异。

  1. final 类似概念: 在 Java 中,final 关键字用于声明不可变的类、方法或变量。在 Python 中,没有类似于 Java 中 final 的关键字来声明不可变性。然而,Python 使用约定来表示某些属性、方法或变量是不应该被修改的,例如使用大写字母表示常量(例如 MY_CONSTANT),但这并不会像 Java 中的 final 那样强制不可变性。

  2. finally 块: 在 Python 中,与 Java 一样,finally 关键字用于定义一个代码块,在 try 块中的代码执行完成后无论是否发生异常都会被执行。这用于确保资源的释放,例如关闭文件或释放资源,以及在程序执行结束时进行清理操作。

  3. __del__ 方法(类似于 finalize): 在 Python 中,对象可以定义一个名为 __del__ 的特殊方法,它类似于 Java 中的 finalize 方法。__del__ 方法会在对象被销毁时(垃圾回收时)自动调用,但在编程中很少使用它,因为它的执行时机和行为不总是可预测的,而且不同于 finally 块。

        总之,尽管 Python 中没有完全等同于 Java 中的 finalfinallyfinalize 的关键字和概念,但它们的某些用法和概念在 Python 中是存在的,并且可以通过约定和特殊方法来实现类似的功能。

6、语句组合形式

  • 可组合
    • try  except可组合
    • try  finally可组合
    • try  except  else可组合
    • try  except  else  finally可组合
  • 不可组合
    • try  else
    • except  else
    • except  finally
    • else finally
    • except  else  finally
    • 任意单独一个不可以

7、Python新特性(except捕获多个异常,使用元祖集合)

捕获多种异常类型并执行相同的操作,可以将这些异常类型放在一个元组中,并使用一个 except 子句来捕获这个元组中的异常类型。例如:

try:
    # 进行数学运算
    print(10 / 0)
    # 控制对象调用方法
    a = None
    a.append(1)
except (ZeroDivisionError, ArithmeticError, AttributeError) as e:
    print(e)
    print("除数为0异常?数学异常?属性异常?都有可能")

        在这个示例中,如果代码块中引发了 ZeroDivisionErrorArithmeticErrorAttributeError 中的任何一种异常,都会进入 except 块并执行相应的处理操作。

8、案例:结合IO流演示try except  finally异常处理语句

# 作   者:JZQ
# 开发时间:2023/8/14 10:25

class ExceptionTest14(object):
    @classmethod
    def divide(cls,dividend,divisor):
        if divisor == 0:
            raise ZeroDivisionError("除数不能为0")
        return dividend / divisor

    @classmethod
    def main(cls):
        try:
            result = cls.divide(10,0)
            print(result)
        except ZeroDivisionError as e:
            print("错误:",e)
        finally:
            print("finally块执行")

if __name__ == '__main__':
    ExceptionTest14.main()
    """
    错误: 除数不能为0
    finally块执行
    """

# 作   者:JZQ
# 开发时间:2023/8/14 10:41

"""
1、except后面的异常类型可以是具体的异常类型,也可以是该异常类型的父类型
2、except可以写多个,建议except的时候,精确的一个一个的处理,这样有利于程序的调试
3、except写多个的时候,从上到下,必须遵守异常类型从小到大的原则
"""
class ExceptionTest07(object):
    @classmethod
    def main(cls):

        #并未捕获到异常,也不会报错
        # try:
        #     f = open("test1.txt","r",encoding="utf-8")
        #     f.close()
        # except ZeroDivisionError as e:
        #     print(e)


        # try:
        #     f = open("test1.txt","r",encoding="utf-8")
        #     f.close()
        # except FileNotFoundError as e:
        #     print(e)
        # print("异常捕获到了,此处代码可执行")

        # try:
        #     f = open("test.txt","r",encoding="utf-8")
        #     print("以上代码出现异常,这里无法执行")
        #     f.close()
        #     #OSError等于WindowsError,均为FileNotFoundError的父类
        # except OSError as e:
        #     print(e)
        # print("异常捕获到了,此处代码可执行")

        # try:
        #     f = open("test.txt","r",encoding="utf-8")
        #     print("以上代码出现异常,这里无法执行")
        #     f.close()
        #     #多态:Exception 为FileNotFoundError的父类
        # except Exception as e:
        #     print(e)

        # #精确处理异常
        # try:
        #     #创建输入流对象
        #     f = open("test.txt","s",encoding="utf-8")
        #     #读文件
        #     f.read(2)
        #     f.close()
        # except FileNotFoundError as e:
        #     print("文件不存在")
        # except TypeError as e:
        #     print("参数传错了")
        # except ValueError:
        #     print("模式传错了")

        #提示报错:except写多个的时候,从上到下,必须遵守异常类型从小到大的原则
        # try:
        #     #创建输入流对象
        #     f = open("test.txt","r",encoding="utf-8")
        #     #读文件
        #     f.read(2)
        #     f.close()
        # except OSError as e:
        #     print("文件不存在")
        # except FileNotFoundError as e:
        #     print("参数传错了")

        #新特性
        try:
            a = None
            a.append(1)
            print(10 / 0)
        except (AttributeError,ZeroDivisionError) as e:
            print(e)

if __name__ == '__main__':
    ExceptionTest07.main()

try:
    # 可能引发异常的代码
    result = 10 / 0
except ZeroDivisionError:
    # 处理特定类型的异常
    print("Division by zero error")
except Exception as e:
    # 处理其他异常
    print("An error occurred:", e)
else:
    # 在没有异常发生时执行
    print("No exceptions occurred")
finally:
    # 无论是否发生异常都执行
    print("Cleanup code")

在这个示例中,如果除以零引发了 ZeroDivisionError 异常,程序将跳转到第一个 except 块,如果引发了其他异常,程序将跳转到第二个 except 块。如果没有异常发生,程序将执行 else 块,然后无论是否发生异常,都会执行 finally 块中的代码。

总之,try, except, else, 和 finally 关键字的组合提供了一种灵活的方式来处理异常,同时确保在异常情况下和正常情况下都能够执行相应的操作。

除了使用内置的异常类,您还可以自定义自己的异常类,继承自内置的异常类或者其他基类。这样您可以根据自己的需求创建特定类型的异常,并在代码中引发和处理这些异常。

以下是一个自定义异常类的示例:

class CustomError(Exception):
    def __init__(self, message):
        self.message = message

try:
    raise CustomError("这是一个自定义异常")
except CustomError as e:
    print("捕获到自定义异常:", e.message)

在这个示例中,我们定义了一个名为 CustomError 的自定义异常类,并在 try 块中引发了这个异常。然后在 except 块中捕获并处理了这个自定义异常,并输出了异常的消息。

六、异常处理的方式(主要针对Exception)

1、概述

Python中异常发生共有两种处理方式:异常上抛和异常捕获

举个例子

  • 我是集团的一个销售员,因为我的失误,到时公司损失了1000元。
  • “损失1000元”可以看做是一个异常发生了。我有两种处理方式。
  • 第一种方式:我把这件事告诉我的领导(异常上抛)
    • 张三 --》李四 --》 王五 --》 CEO
  • 第二种方式:我自己掏腰包把这个钱补上(异常的捕捉)

异常发生之后,如果选择了上抛,抛给了调用者,调用者需要对这个异常继续处理,调用者处理该异常同样有两种方式:异常上抛和异常捕获,就这样一级一级的处理,直到将这个异常解决。

1.1、异常和处理方式之间的关系

1.2、处理异常时不论是捕获还是上抛目标异常对象类型应为实际异常对象类型或其父类型(否则没有意义,还是没有真正处理异常) 

例如异常:

FileNotFoundError --> OSError --> Exception --> BaseException

只要捕获其中一个类型就可以,因为其他三个都是FileNotFoundError 类的父类

try:
    f = open("aaa","r")
except (FileNotFoundError,OSError,Exception,BaseException) as e:
    #只要捕获其中一个类型就可以,因为其他三个都是FileNotFoundError 类的父类
    print(e)

2、上抛异常(每个方法或者函数都默认上抛Exception 运行时异常)

Python是动态语言,没有显示的编译阶段,因此只有运行时异常,也不需要像java那样显示用throws抛出编译时异常

1、抛给上一级方法调用者,谁调用我,我就抛给谁,抛给上一级

2、注意:Python中异常发生之后如果一直上抛,最终抛给了调用者PVM,PVM知道这个异常发生,只有一个结果。终止Python程序的执行

3、异常上抛类似于推卸责任,继续把异常传递给调用者

2.1、运行时异常自动上抛

所有方法和函数都默认上抛Exception(运行时异常)

class ExceptionTest03(object):

    @classmethod
    def main(cls):
        """
        程序执行到此处发生了ZeroDivisionError异常
        底层PVM创建了一个ZeroDivisionError异常对象,然后抛出了
        由于是main方法调用了 10 / 0 ,所以这个异常ZeroDivisionError抛给了main方法
        main方法没有处理,将这个异常自动上抛给了PVM,PVM最终终止了程序的运行

        ZeroDivisionError继承与Exception,属于运行时异常
        Python中只有运行时异常Exception,在编写程序阶段对所有的异常都不需要预先处理
        """
        print(10 / 0)

        #这行代码没有执行
        print("hello world")

if __name__ == '__main__':
    ExceptionTest03.main()

3、捕获异常(Catch Exception)

1、使用try.. ..except语句进行异常的捕获

2、这件事发生了,但谁也不知道,因为我给抓住了

3、异常捕获等于把异常拦下了,异常真正的解决了,调用者是不知道的

3.1、运行时异常手动捕获

class ExceptionTest03(object):

    @classmethod
    def main(cls):
        try:
            print(10 / 0)
        except ZeroDivisionError as e:
            print("除数不能为0: ",e)
        #异常处理后,这行代码可以执行了
        print("hello world")

if __name__ == '__main__':
    ExceptionTest03.main()
    """
    除数不能为0:  division by zero
    hello world
    """

4、异常处理方式的选择

  • 默认上抛异常
  • 一般情况下使用捕获,增强程序的健壮性(一般方法第一级调用处使用异常捕获,避免上抛到PVM,导致程序终止)

七、出现异常后的代码执行情况

  • 1、代码执行过程中出现异常,则自出现异常处起,后续代码均不会执行,在该异常被捕获到后,才可执行后续代码(上抛异常不行,必须要被捕获,消化掉,真正的解决该异常)
  • 2、只要异常没有捕获,采用了上抛的方式,此方法的后续代码不会执行
    • 定义可能抛出异常的方法时,由于执行了raise语句,抛出了异常,该方法终止,进行了异常上抛
    • 调用可能抛出异常的方法时,由于没有进行异常捕获处理,调用者方法终止,进行了异常上抛
  • 3、另外需要注意,try语句块中的某一行出现异常,该行后面的代码不会执行
  • 4、try  except 捕获异常后,后续代码可以执行

1、案例:演示方法多次调用异常上抛(raise 执行方法终止)
 

# 作   者:JZQ
# 开发时间:2023/8/14 19:50

"""
处理异常的第一种方式:
    Python中都是运行时异常,默认上抛

处理异常的第二种方式:
    使用try except语句对异常进行捕获
    这个异常就不会上抛,自己把这个异常处理了
    异常到此为止,不再上抛了
"""
class ExceptionTest06(object):
    """
    一般不建议在第一级调用方法上使用默认上抛,因为这个异常如果真正的发生了,一定会上抛给PVM,PVM只有终止程序
    异常处理机制的作用就是增强程序的健壮性,怎么能做到,异常发生了,也不影响程序的执行
    所以,一般第一级调用方法中异常建议使用try  except进行捕捉,就不要再往上抛了
    """
    @classmethod
    def main(cls):
        print("main begin")

        # 10 / 0 是算数异常,Python中都是运行时异常,没有强制在编写程序阶段必须进行处理,因此可以处理也可以不处理
        # try:
        #     print(10 / 0)
        # except ZeroDivisionError as e:
        #     print(e)

        try:
            #try尝试
            cls.m1()
            #以上代码出现异常,直接进入except语句块执行
            print("hello")
        except OSError as e: #捕获FileNotFoundError类型的异常对象并起别名为e,就是赋值给e
            #这个分支中可以使用e引用,e引用保存的内存地址就是 FileNotFoundError类型异常对象的内存地址
            #发生异常后执行的代码块
            print("文件不存在:",e) #文件不存在: [Errno 2] No such file or directory: 'aaa'
            # raise  #一般就不在重新上抛了
        print("main over")

    @classmethod
    def m1(cls):  #默认进行Exception异常上抛
        print("m1 begin")
        cls.m2()
        print("m1 over")

    @classmethod
    def m2(cls):  #默认进行Exception异常上抛
        print("m2 begin")
        cls.m3()
        print("m2 over")

    @classmethod
    def m3(cls):  #默认进行Exception异常上抛
        print("m3 begin")
        f = open("aaa","r",encoding="utf-8")
        print("以上代码出现异常,此处也不会执行")
        print("m3 over")

if __name__ == '__main__':
    ExceptionTest06.main()
    """
    main begin
    m1 begin
    m2 begin
    m3 begin
    文件不存在: [Errno 2] No such file or directory: 'aaa'
    main over
    """

八、异常对象的常用方法(实例方法)

在Python中,异常是用于处理程序运行时错误或异常情况的机制。异常对象是表示错误信息的对象,它们可以具有一些常用的实例方法来获取有关错误信息和处理异常的详细信息。以下是一些常用的异常对象实例方法

1、exception.args:返回一个包含异常参数的元组,这些参数是在引发异常时传递给异常构造函数的。

try:
    x = 1 / 0
except ZeroDivisionError as e:
    print(e.args)  # 输出: ('division by zero',)

2、exception.with_traceback(tb):将异常与指定的回溯(traceback)关联起来,允许您在另一个位置重新引发异常。

exception.with_traceback(tb) 是一个用于异常对象的方法,它允许您将异常与指定的回溯(traceback)信息关联起来。回溯是一个记录了程序调用栈的信息,可以帮助您定位引发异常的代码位置。使用 with_traceback() 方法,您可以重新引发一个异常,并且可以为新的异常提供一个自定义的回溯信息,从而改变异常的上下文信息。

这个方法通常在异常处理和异常转换的情况下使用,以便更好地追踪和报告错误。

以下是使用 with_traceback() 方法的示例:

try:
    x = 1 / 0
except ZeroDivisionError as original_exception:
    # 创建一个新的异常对象,并将其与新的回溯信息关联起来
    new_exception = original_exception.with_traceback(None)

    # 将新异常重新引发,将更新的回溯信息记录下来
    # raise new_exception
    print(new_exception) #division by zero

        在上面的示例中,我们首先捕获了一个 ZeroDivisionError 异常,并将其存储在 original_exception 变量中。然后,我们使用 with_traceback(None) 创建了一个新的异常对象 new_exception,这个新异常对象没有回溯信息。最后,我们通过重新引发 new_exception,将异常的上下文重新设置为新的回溯信息,从而实现了异常的重新引发并关联了新的回溯信息。

        需要注意的是,with_traceback() 方法并不会修改原始异常对象,而是返回一个新的异常对象。这可以用于在异常处理过程中重新构造异常,以提供更精确的错误信息和上下文信息。

        总而言之,exception.with_traceback(tb) 方法允许您控制异常的回溯信息,从而更好地调试和处理程序中的错误情况。

3、exception.__str__():返回异常的人类可读的字符串表示,通常用于打印错误信息

try:
    x = 1 / 0
except ZeroDivisionError as e:
    print(e)  # 输出: division by zero
    print(str(e))  # 输出: division by zero

4、exception.__class__:(属性)返回异常的类对象,可以用于确定异常的类型。

try:
    x = 1 / 0
except ZeroDivisionError as e:
    print(e.__class__)  # 输出: <class 'ZeroDivisionError'>

        这些是一些常见的异常对象实例方法,它们允许您获取有关异常的信息并进行适当的处理。请注意,异常对象可能会有其他属性和方法,具体取决于异常的类型。您可以通过查阅Python官方文档或使用内置的dir()函数来查看特定异常对象的可用方法和属性。

九、自定义异常类

1、概述

        Python内置的异常类肯定是不够用的,在实际的开发中,有很多业务,这些业务出现异常之后,Python中都是没有的,和业务挂钩的,因此我们要自定义异常类

2、自定义异常类的步骤

第一步:创建一个继承自 Exception 类(或其子类)的新类,然后为该类添加你自己的属性和方法(Python中只有运行时异常,简称为异常)。

第二步:为异常类添加初始化方法(__init__): 在初始化方法中,你可以传递一些参数来自定义异常消息或其他相关信息通常,异常类的初始化方法会调用父类的 __init__ 方法

3、自定义异常(继承Exception)

class MyError(Exception):
    #定义一个代码str类型参数message的构造方法
    def __init__(self,message):
        #调用父类的构造方法
        super().__init__(message)

测试:

from 自定义异常.my_error import MyError

class Test(object):
    @classmethod
    def main(cls):
        raise MyError("自定义异常")

if __name__ == '__main__':
    Test.main()
    """
    Traceback (most recent call last):
    File "D:\pythonProject\Basic\exception\exception_test14.py", line 36, in <module>
    Test.main()
    File "D:\pythonProject\Basic\exception\exception_test14.py", line 33, in main
    raise MyError("自定义异常")
    自定义异常.my_error.MyError: 自定义异常
    """

4、自定义异常在实际开发中的作用

自定义异常类

# 作   者:JZQ
# 开发时间:2023/8/15 11:11
class MyStackOperationError(Exception):
    def __init__(self,message):
        super().__init__(message)

list列表模拟栈数据结构

# 作   者:JZQ
# 开发时间:2023/8/15 11:13
from typing import List
from my_stack_operation_error import MyStackOperationError

"""
/**
 * 编写程序:使用一维数组,模拟栈数据结构
 * 要求:
 * 1、这个栈可以存储Python中的任何引用数据类型
 * 2、在栈中提供push方法模拟压栈。(栈满了,要有提示信息)
 * 3、在栈中提供pop方法模拟弹栈。(栈空了,也要有提示信息)
 * 4、编写测试程序,new栈对象,调用push,pop方法模拟压栈和弹栈动作。
 */
"""

class MyStack(object):

    # 构造方法
    def __init__(self):
        """
             /**
             * 向栈当中存储元素,我们这里使用一维数组模拟,存到栈中,就表示存储到数组中
             * 因为数组是我们学习java的第一个容器
             * 为什么选择Object类型数组?因为这个栈可以存储java中任何引用类型数据
             */
        """
        self.__objects: List[object] = [None] * 10  #列表默认初始化容量为10

        """
             /**
             * 模拟栈顶指针,永远指向栈顶元素
             * 默认初始值为0,注意:最初的栈是空的,一个元素也没有
             *
             * 如果默认初始化为0,则表示栈顶指针指向了顶部元素的上方
             * 如果默认初始化为-1,则表示栈顶指针指向了顶部元素
             */
        """
        self.__index: int = -1

    # setter and getter
    def setObjects(self, objects):
        self.__objects = objects

    def getObjects(self):
        return self.__objects

    # setter and getter
    def setIndex(self, index):
        self.__index = index

    def getIndex(self):
        return self.__index


    def push(self, obj):
        """
        压栈的方法
        :param obj: 要往栈中压的元素
        :return:
        """
        if self.getIndex() >= self.getObjects().__len__() - 1:
            raise MyStackOperationError("压栈失败,栈已满")
        # 栈顶指针向上加1
        self.setIndex(self.getIndex() + 1)
        # 列表中的元素修改为添加的元素
        self.getObjects()[self.getIndex()] = obj
        print(f"压栈 {obj} 元素成功,栈顶指针指向:{self.getIndex()}")

    def pop(self):
        """
        弹栈的方法
        :return: 返回所弹出的元素对象
        """
        if self.getIndex() < 0:
            raise MyStackOperationError("弹栈失败,栈已空")
        print(f"弹栈 {self.getObjects()[self.getIndex()]} 元素成功,",end="")
        #栈顶指针向下移动一位
        self.setIndex(self.getIndex() - 1)
        print(f"栈顶指针指向:{self.getIndex()}")
        #程序执行到此处说明栈没有空,返回弹出的元素
        return self.getObjects()[self.getIndex() + 1]

class TestMyStack(object):
    @classmethod
    def main(cls):
        #创建一个栈对象
        my_stack = MyStack()

        #模拟压栈操作
        for i in range(11):
            try:
                my_stack.push(i)
            except MyStackOperationError as e:
                print(e)
        #模拟弹栈操作
        for i in range(11):
            try:
                print("返回元素:", my_stack.pop())
            except MyStackOperationError as e:
                print(e)

if __name__ == '__main__':
    TestMyStack.main()
    """
    压栈 0 元素成功,栈顶指针指向:0
    压栈 1 元素成功,栈顶指针指向:1
    压栈 2 元素成功,栈顶指针指向:2
    压栈 3 元素成功,栈顶指针指向:3
    压栈 4 元素成功,栈顶指针指向:4
    压栈 5 元素成功,栈顶指针指向:5
    压栈 6 元素成功,栈顶指针指向:6
    压栈 7 元素成功,栈顶指针指向:7
    压栈 8 元素成功,栈顶指针指向:8
    压栈 9 元素成功,栈顶指针指向:9
    压栈失败,栈已满
    
    弹栈 9 元素成功,栈顶指针指向:8
    返回元素: 9
    弹栈 8 元素成功,栈顶指针指向:7
    返回元素: 8
    弹栈 7 元素成功,栈顶指针指向:6
    返回元素: 7
    弹栈 6 元素成功,栈顶指针指向:5
    返回元素: 6
    弹栈 5 元素成功,栈顶指针指向:4
    返回元素: 5
    弹栈 4 元素成功,栈顶指针指向:3
    返回元素: 4
    弹栈 3 元素成功,栈顶指针指向:2
    返回元素: 3
    弹栈 2 元素成功,栈顶指针指向:1
    返回元素: 2
    弹栈 1 元素成功,栈顶指针指向:0
    返回元素: 1
    弹栈 0 元素成功,栈顶指针指向:-1
    返回元素: 0
    弹栈失败,栈已空
    """

5、用户注册案例

编写程序模拟用户注册:

1、程序开始执行时,提示用户输入“用户名”和“密码”信息

2、输入信息之后,后台Python程序模拟用户注册

3、注册时用户要求长度在[6,14]之间,小于或者大于都表示异常

# 作   者:JZQ
# 开发时间:2023/8/15 14:48
from invalid_name_error import InvalidNameError

class UserService(object):
    def register(self,userName:str,passWord:str):
        if None == userName or userName.__len__() < 6 or userName.__len__() > 14:
            raise InvalidNameError("用户名不合法,长度必须在[6,14]之间")
        #程序执行到此处说明userName合法
        print(f"注册成功,欢迎{userName}")

class Test(object):
    @classmethod
    def main(cls):
        userName = input("请输入用户名:")
        passWord = input("请输入密码:")

        userService = UserService()
        try:
            userService.register(userName,passWord)
        except InvalidNameError as e:
            print(e)

if __name__ == '__main__':
    Test.main()

十、综合案例:武器库数组案例(导入自定义模块切记使用相对导入)

       写一个类Army,代表一支军队,这个类有一个属性Weapon数组w(用来存储该军队所拥有的武器),该类提供一个构造方法,在构造方法中通过传一个int类型的参数来限定该类型所能拥有的最大武器数量

       该类还提供一个方法addWeapon(Weapon weapon),表示吧参数weapon所代表的武器加入到数组w中。在这个类中还定义两个方法attackAll() 让w数组中的所有武器攻击。以及moveAll()让w数组中的所有可移动的武器移动

写一个主方法去测试以上程序。

提示:

Weapon类是一个父类,应该有很多子武器

这些武器应该有一些事可移动的,有一些是可攻击的

1、武器父类:weapon.py

# 作   者:JZQ
# 开发时间:2023/8/15 15:29

class Weapon(object):
    # def move(self):
    #     pass
    #
    # def shoot(self):
    #     pass
    pass

2、接口:moveable.py

# 作   者:JZQ
# 开发时间:2023/8/15 15:29
from abc import ABC, abstractmethod

#可移动的接口
class Moveable(ABC):

    @abstractmethod
    def move(self):
        """
        移动的方法
        :return:
        """
        pass

3、接口:shootable.py

# 作   者:JZQ
# 开发时间:2023/8/15 15:40
from abc import ABC,abstractmethod

#射击接口
class Shootable(ABC):
    @abstractmethod
    def shoot(self):
        """
        射击行为
        :return:
        """
        pass

4、武器:fighter.py

# 作   者:JZQ
# 开发时间:2023/8/15 15:42

from weapon import Weapon
from moveable import Moveable
from shootable import Shootable

"""
战斗机,是武器,可移动,可射击
"""
class Fighter(Weapon,Moveable,Shootable):

    def move(self):
        print("战斗机起飞")

    def shoot(self):
        print("战斗机开炮")

if __name__ == '__main__':
    f = Fighter()
    # f.move()
    # f.shoot()
    # print(isinstance(f,Shootable))
    # print(issubclass(f.__class__,Shootable))
    # print(f)

    if isinstance(f,Shootable):
        f.shoot()
    if isinstance(f,Moveable):
        f.move()

5、武器:gaoshepao.py

# 作   者:JZQ
# 开发时间:2023/8/15 15:58

from weapon import Weapon
from shootable import Shootable

"""
高射炮,是武器,可射击,不能移动
"""
class GaoShePao(Weapon,Shootable):
    def shoot(self):
        print("高射炮开炮")

if __name__ == '__main__':
    GaoShePao().shoot()

6、武器:tank.py

# 作   者:JZQ
# 开发时间:2023/8/15 16:02
from weapon import Weapon
from moveable import Moveable
from shootable import Shootable

"""
坦克,是武器,可移动,可射击
"""
class Tank(Weapon,Moveable,Shootable):
    def move(self):
        print("坦克移动")
    def shoot(self):
        print("坦克开炮")

if __name__ == '__main__':
    t = Tank()
    t.move()
    t.shoot()

    print(isinstance(t,Moveable))

7、武器:wuzifeiji.py

# 作   者:JZQ
# 开发时间:2023/8/15 16:05
from weapon import Weapon
from moveable import Moveable

"""
物资飞机,是武器,可移动,不可射击
"""
class WuZiFeiJi(Weapon,Moveable):
    def move(self):
        print("运输机起飞")

if __name__ == '__main__':
    WuZiFeiJi().move()

8、自定义异常类:add_weapon_error.py

# 作   者:JZQ
# 开发时间:2023/8/15 16:07
class AddWeaponError(Exception):
    def __init__(self,message):
        super().__init__(message)

if __name__ == '__main__':
    try:
        raise AddWeaponError("111")
    except AddWeaponError as e:
        print(e)

9、军队:army.py

# 作   者:JZQ
# 开发时间:2023/8/15 16:08
from typing import List

from moveable import Moveable
from shootable import Shootable
from add_weapon_error import AddWeaponError
from weapon import Weapon

"""
军队
"""
class Army(object):

    def __init__(self,count):
        """
        创建军队的构造方法
        初始化数组中的每一个元素默认为None
        武器数组是有了,但是武器数组中还没有放武器
        :param count:
        """
        self.weapons:List[Weapon] = [None] * count

    def add_weapon(self,weapon):
        """
        将武器添加进武器数组中
        :param weapon:
        :return:
        """
        for i in range(self.weapons.__len__()):
            if None == self.weapons[i]:
                #在数组空的位置上添加武器
                self.weapons[i] = weapon
                print(f"{weapon} 添加成功")
                return
        #程序执行到此处说明武器库已经满了
        raise AddWeaponError("武器数量已达到上限")

    def attack_all(self):
        """
        所有可攻击的武器攻击
        :return:
        """
        for i in range(self.weapons.__len__()):
            # print(self.weapons[i])
            # print(isinstance(self.weapons[i],Shootable))
            if isinstance(self.weapons[i],Shootable):
                shootable:Shootable = self.weapons[i]
                shootable.shoot()
                # self.weapons[i].shoot()

    def move_all(self):
        """
        所有可移动的武器移动
        :return:
        """
        for i in range(self.weapons.__len__()):
            if isinstance(self.weapons[i],Moveable):
                moveable:Moveable = self.weapons[i]
                moveable.move()

10、测试程序:test.py

# 作   者:JZQ
# 开发时间:2023/8/15 16:30
from add_weapon_error import AddWeaponError
from army import Army
from fighter import Fighter
from gaoshepao import GaoShePao
from tank import Tank
from wuzifeiji import WuZiFeiJi

class Test(object):
    @classmethod
    def main(cls):
        #创建军队对象
        army = Army(4)

        #创建武器对象
        fighter = Fighter()
        fighter1 = Fighter()
        tank = Tank()
        wuziFeiJI = WuZiFeiJi()
        gaoShePao = GaoShePao()

        #添加武器
        try:
            army.add_weapon(fighter)
            # army.add_weapon(fighter1)
            army.add_weapon(tank)
            army.add_weapon(wuziFeiJI)
            army.add_weapon(gaoShePao)
            army.add_weapon(gaoShePao)
        except AddWeaponError as e:
            print(e)

        print("=" * 50)
        #所有可攻击的武器攻击
        army.attack_all()
        print("=" * 50)
        #所有可移动的武器移动
        army.move_all()

if __name__ == '__main__':
    Test.main()

    # from Basic.exception.案例.武器库数组案例.shootable import Shootable
    # from shootable import Shootable
    # fighter = Fighter()
    # print(isinstance(fighter,Shootable))

为什么这样 print(type(ZeroDivisionError)) #<class 'type'>

在 Python 中,ZeroDivisionError 是一个内置的异常类,它是继承自 BaseException 的一个子类。当发生除以零的操作时,Python 解释器会引发 ZeroDivisionError 异常。因此,ZeroDivisionError 是一个类,而不是一个实例对象。

当您使用 type(ZeroDivisionError) 时,您实际上在获取 ZeroDivisionError 类的类型。在 Python 中,所有类的类型都是 type。这就是为什么 print(type(ZeroDivisionError)) 输出 <class 'type'>

这种行为反映了 Python 中的类层次结构和类型体系。每个类本身也是一个对象,并且它们的类型都是 type,因为类是从 type 派生出来的。所以,ZeroDivisionError 类的类型是 type

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值