异常
介绍
概述
我们把程序中出现的所有非正常情况, 统称为: 异常. 俗称叫: Bug.
程序默认处理异常方式
程序会将异常的类型, 产生的原因, 以及异常的位置, 打印到控制台上.并终止程序的执行.
问: 如何实现, 解决异常之后, 让程序继续往下执行呢?答: 可以通过 捕获异常: try.except.finally 这种方式来实现.
演示异常
# 你可能会遇到的一些异常. print(10 // 0) # 除数不能为 0, ZeroDivisionError src_f = open('1.txt', 'r') # 文件不存在, FileNotFoundError print(src_f) print('看看我执行了吗!')
捕获异常
概述
指的是 try.except.finally 这个语法, 即: 手动处理异常.
特点
手动处理之后, 程序会继续向下执行.
格式
try: 里边写可能出问题的代码except: 如果出问题了, 这里写的是 处理方式
格式解释
-
先执行try中的内容, 看有无问题.
-
有问题, 则会立即跳转到 except语句中 执行.
-
无问题, 则不会执行 except中的内容.
-
无论是否有问题, 处理之后, 程序都会自动向下执行.
演示
# 需求: 尝试去读取 1.txt 文件中的数据, 如果文件不存在, 我们就创建. try: # 这里写的是: 可能出问题的代码 print('----- try 1 -----') src_f = open('1.txt', 'r') # 这行代码有Bug print('----- try 2 -----') print(src_f) except: # 这里写的是, 出问题后的解决方案 # print('哎呀, 程序出问题了!') # 走这里, 说明文件不存在, 我们就创建它. src_f = open('1.txt', 'w') print('----- except -----') print('看看我执行了吗!')
捕获指定异常
格式
try: 里边写可能出问题的代码 except [Exception as e]: # 这里的[]表示 可选 如果出问题了, 这里写的是 处理方式
演示
# 需求: 尝试去读取 1.txt 文件中的数据, 如果文件不存在, 我们就创建. # 场景1: 捕获指定的异常. try: # 这里写的是: 可能出问题的代码 print('----- try 1 -----') src_f = open('1.txt', 'r') # 这行代码有Bug print('----- try 2 -----') except FileNotFoundError: # 这里写的是, 出问题后的解决方案 print('----- except 1 -----') src_f = open('1.txt', 'w') print('----- except 2 -----') # 场景2: 捕获异常时, 异常的类型 和 异常出现的类型不匹配, 则: 捕获不到该异常. try: # 这里写的是: 可能出问题的代码 print('----- try 1 -----') src_f = open('1.txt', 'r') # 这行代码有Bug print('----- try 2 -----') except ZeroDivisionError: # 这里写的是, 出问题后的解决方案 print('----- except 1 -----') src_f = open('1.txt', 'w') print('----- except 2 -----') # 场景3: 捕获多个异常. try: # 这里写的是: 可能出问题的代码 print('----- try 1 -----') # src_f = open('1.txt', 'r') # 这行代码有Bug print(age) print(10 // 0) # 这行代码有Bug print('----- try 2 -----') except (ZeroDivisionError, FileNotFoundError, NameError): # 这里写的是, 出问题后的解决方案 print('哎呀, 程序出错了!') # 场景4: 捕获所有的异常, 因为Python中的异常类型太多了, 我们不可能逐一编写(除非业务固定, 出的问题也固定), 一般会直接写: Exception try: # 这里写的是: 可能出问题的代码 print('----- try 1 -----') src_f = open('1.txt', 'r') # 这行代码有Bug print(age) print(10 // 0) # 这行代码有Bug print('----- try 2 -----') except Exception: # Exception是所有异常的父类, 即: 它代表所有的异常. # 这里写的是, 出问题后的解决方案 print('哎呀, 程序出错了!') # 场景5: 打印异常的信息. try: # 这里写的是: 可能出问题的代码 print('----- try 1 -----') # src_f = open('1.txt', 'r') # 这行代码有Bug # print(age) print(10 // 0) # 这行代码有Bug print('----- try 2 -----') except Exception as e: # Exception是所有异常的父类, 即: 它代表所有的异常. # 这里写的是, 出问题后的解决方案 # print('哎呀, 程序出错了!') print(f"程序出错了, 原因是: {e}") print('看看我执行了吗!')
捕获异常完整写法
格式
try: 里边写可能出问题的代码 except [Exception as e]: # 这里的[]表示 可选 如果出问题了, 这里写的是 处理方式 else: 如果try中的内容无问题, 则会执行这里. finally: 无论try中是否有问题, 都会执行这里的内容.
执行流程
-
先执行try中的内容, 看有无问题.
-
有问题, 则会立即跳转到 except语句中 执行.
-
无问题, 则try中内容执行完毕后, 会跳转到else中执行.
-
无论是否有问题, 最终都会执行finally中的内容.
-
大白话总结:程序有问题: try => except => finally程序无问题: try => else => finally
演示
# 需求1: 演示 try.except完整格式代码. try: print('----- try 1 -----') # print(10 // 0) print(10 // 2) print('----- try 2 -----') except Exception as e: print(f'程序出问题了, 原因是: {e}') else: print('很开心, 程序没有Bug, 我是else!') finally: print('你们都在说什么, 无论你们执不执行, 我都会执行, 我是finally!') print('-' * 30) # 需求2: 加入异常处理后的, 文件备份的代码. src_f = open('data/3.jpg', 'rb') dest_f = open('data/4.jpg', 'wb') while True: data = src_f.read(8192) if not data: break dest_f.write(data) src_f.close() dest_f.close() # 标记变量. flag = False # False => 文件对象创建失败. True => 文件对象创建成功. try: # 可能出问题的代码 src_f = open('data/1.jpg', 'rb') dest_f = open('d ata/4.jpg', 'wb') flag = True # 说明 文件对象创建成功. except Exception as e: # 出问题后的解决方案 print(f'程序出错了, 原因是: {e}') else: # try中代码无问题, 则会执行这里. while True: data = src_f.read(8192) if not data: break dest_f.write(data) finally: # 无论try中是否有问题, 最终都会走这里, 一般用于: 释放资源. if flag: src_f.close() dest_f.close() print('文件对象已关闭!') print('程序执行结束了!')
异常传递
概述
异常是具有传递性的, 如果在某个函数内部有Bug, 且该函数没有处理, 则会传给该函数的调用者来处理.依次类推, 逐级传递, 直至传到main函数中, 如果还不处理, 程序就会自动处理. 即: 先打印异常的类型, 描述信息, 位置到控制台上, 并终止程序的运行.
细节
-
如果为了保证所有的异常都被处理了, 我们可以在main函数中对异常做处理.
-
建议异常越早处理越好, 在哪里发生, 就在哪里处理.
演示
# 需求: 定义函数fun01(), 里边有Bug. 然后由fun02()函数调用fun01()函数, 且在main函数中调用 fun02()函数, 观察会出现什么情况. # 1. 定义fun01()函数 def fun01(): print('--- fun01函数 start ---') # 情况4: 在fun01函数中, 处理异常. try: # 有Bug print(10 // 0) except: pass print('--- fun01函数 end ---') # 2. 定义fun02()函数, 调用fun01()函数 def fun02(): print('--- fun02函数 start ---') # 情况3: 在 fun02函数中, 处理异常. # 调用fun01()函数 fun01() try: fun01() except: pass print('--- fun02函数 end ---') # 3. 在main函数中, 测试上述的内容. if __name__ == '__main__': # 情况1: 直接调用 fun02()函数, 看看结果是啥. fun02() # 情况2: 在main中处理异常, 看看结果是啥. try: # 可能出问题的代码 fun02() except: # 出现问题后的解决方案 pass # 情况3: 在fun02()函数中, 处理异常, 看看结果是啥. fun02() # 情况4: 在fun01()函数中, 处理异常, 看看结果是啥. fun02() print('看看我执行了吗!')
模块
内置模块介绍
概述
Python中的, 模块 = 1个Python文件 = .py文件.模块中可以写 函数, 类, 变量, 测试代码...
导入模块的方式
-
import 模块名A. 可以导入 模块下 所有的功能(函数)B. 必须通过 模块名.功能名() 的方式来调用.
-
import 模块名 as 别名A. 可以导入 模块下 所有的功能(函数)B. 必须通过 别名.功能名() 的方式来调用.
-
from 模块名 import 功能名A. 只能导入 模块下 指定的功能(函数)B. 可以通过 功能名() 的方式 来直接调用.
-
from 模块名 import 功能名 as 别名A. 只能导入 模块下 指定的功能(函数)B. 可以通过 别名() 的方式 来直接调用.
-
from 模块名 import *A. 可以导入 模块下 所有的功能(函数)B. 可以通过 功能名() 的方式 来直接调用.
演示
# 此处以 time模块下的 time()函数, sleep()函数举例. # 演示: 1. import 模块名 import time print(time.time()) # 时间戳, 打印当前时间的秒值, 即: 从时间原点(1970年1月1日 08:00:00) 到 当前时间的 秒值. print('start') time.sleep(3) # 休眠 3 秒 print('end') # 演示: 2. import 模块名 as 别名 import time as t print(t.time()) # 时间戳 print('start') t.sleep(3) # 休眠 3 秒 print(t.localtime()) # 打印当前时间. print('end') # 演示: 3. from 模块名 import 功能名 from time import time, sleep print(time()) # 时间戳 print('start') sleep(2) # 休眠 2 秒 print('end') # print(localtime()) # 报错, 因为没有导入, 无法直接使用. # 演示: 4. from 模块名 import 功能名 as 别名 from time import time as t, sleep as ai30 print(t()) # 时间戳 print('start') ai30(2) # 休眠 2 秒 print('end') # print(localtime()) # 报错, 因为没有导入, 无法直接使用. # 演示: 5. from 模块名 import * from time import * print(time()) # 时间戳 print('start') sleep(2) # 休眠 2 秒 print('end') print(localtime()) # 报错, 因为没有导入, 无法直接使用.
自定义模块演示
my_module1.py
# 自定义模块, 模块名叫: my_module1, 模块名要做到见名知意, 否则别的模块可能无法调用. # 在当前模块中可以定义功能(函数), 变量, 类.... """扩展: __name__ 属性, 在当前模块中打印的是: __main__, 在调用者中打印的是: 被调用的模块名 即: __name__属性可以避免: 在调用者中 执行被调用者的 测试代码 这种情况. """ # 功能 = 函数 def fun01(): print(__name__) # 当前模块中, 打印的是 '__main__', 调用者中打印的是: (被调用的模块)当前的模块名 print('--- my_module1 fun01 函数 ---') def fun02(): print(__name__) print('--- my_module1 fun02 函数 ---') # 在当前模块中, 对上述的功能(函数)做测试. # 如果这个if判断条件成立, 说明: 是在当前代码中执行如下的测试内容的. if __name__ == '__main__': fun01() fun02()
my_module2.py
# 自定义模块, 模块名叫: my_module2, 模块名要做到见名知意, 否则别的模块可能无法调用. """ __all__属性: 1. 它只针对于 from 模块名 import * 有效. 2. 如果不写 all属性, 则 from 模块名 import * 会导入该模块下所有的功能(函数). 3. 如果写了 all属性, 则 from 模块名 import * 只会导入 all属性写的函数. 4. 如果1个模块中的函数较多, 且常用的函数都是固定的, 就可以考虑使用 all属性. """ # 这行代码意思是: from 模块名 import * 的时候, 只会导入 fun01函数. __all__ = ['fun01'] # 功能 = 函数 def fun01(): print(__name__) # 当前模块中, 打印的是 '__main__', 调用者中打印的是: (被调用的模块)当前的模块名 print('--- my_module2 fun01 函数 ---') def fun02(): print(__name__) print('--- my_module2 fun02 函数 ---') # 在当前模块中, 对上述的功能(函数)做测试. # 如果这个if判断条件成立, 说明: 是在当前代码中执行如下的测试内容的. if __name__ == '__main__': fun01() fun02()
测试.py
# 案例: 演示调用自定义模块的内容. # 1. 导包. import my_module1 as m1 import my_module2 as m2 import my_module1 as m1, my_module2 as m2 # 2. 调用m1模块的功能 m1.fun01() m1.fun02() print('=' * 30) # 3. 调用m2模块的功能. m2.fun01() m2.fun02() # 一种特殊情况, 同时导入多个模块, 且模块中有重名函数, 则会使用后导入模块的函数. from my_module1 import fun01 from my_module2 import fun01 fun01() # 打印的是 my_module2模块的函数. # 演示 __all__属性, 它只针对于 from 模块名 import * 有效. from my_module2 import * fun01() fun02() # 报错, 因为 my_module2的 all属性没写该函数名 # all属性只针对于: from 模块名 import * 有效. from my_module2 import fun01, fun02 fun01() fun02()
包
概述
包也叫Package, 你看起来它好像是1个文件夹, 只不过多了一个 init.py文件.包中可以有模块, 且包可以像模块一样被导入.
包 => 模块 => 类, 变量, 功能
细节
-
大白话翻译, 包 = 文件夹 + init.py
-
我们可以把多个功能相似的模块(例如都是操作容器类型的, 或者都是操作文件的), 放到1个包中, 方便我们集中管理.
-
包的本质可以看做是模块, 也可以 导入使用.
导包的两种方式
-
import 包名.模块名A. 只能导入 包下的某个模块中 所有的内容.B. 必须通过 包名.模块名.功能名() 的方式来调用.
-
from 包名 import 模块名A. 只能导入 包下的指定模块中 所有的内容.B. 可以通过 模块名.功能名() 的方式来调用.
演示
代码
# 需求: 自定义my_package包, 里边有 my_module1 和 my_module2两个模块. 测试: 该包的功能. # # 导包的方式1, 演示. import my_package.my_module1 # 必须通过 包名.模块名.功能名() 的方式来调用. # 包名 模块名 功能名(函数名) my_package.my_module1.fun01() my_package.my_module1.fun02() print('-' * 30) # # 导包的方式2, 演示. from my_package import my_module2 # 可以通过 模块名.功能名() 的方式来调用. my_module2.fun01() my_module2.fun02() print('-' * 30) # 导包的方式3: from ... import * from my_package import * # mypackage包下的 my_module1模块. my_module1.fun01() my_module1.fun02() print('-' * 30) # mypackage包下的 my_module2模块. my_module2.fun01() my_module2.fun02()
包文件
扩展Debug
概述
它是程序员的神级技能, 可以帮助我们查看程序的执行流程, 排错等...
步骤
-
打断点.那里不会点哪里, 一般是第一行(部分系统是第2行)有效代码处.
-
如何开启Debug断点调试.在代码编辑区, 右键 => Debug ...
-
如何查看调试信息.下左: MainThread, 类似于 栈, 用于显示: 代码执行到哪里了.下中: Variables, 显示变量值变化过程的.下右: Consoles, 显示程序的运行结果的.
-
如何进行下一步.F7: 逐过程调试, 一步一步的走, 遇到自定义函数的时候, 会进到函数的内部.F8: 逐行调试, 一行一行的走, 遇到自定义函数的时候, 不会进到函数的内部, 而是当做一行代码来处理.F9: 逐断点调试, 一个断点一个断点的跳, 如果当前断点已经是最后1个, 再次按下F9则会结束调试.
-
如何结束调试.场景1: 调试完.场景2: 手动终止.场景3: 在已经是最后1个断点的情况下, 再次按下F9, 则会结束调试.
-
如何删除断点.场景1: 再次点击即可.场景2: 选择 break points图标(双点图标).
测试用代码
# 1. 定义fun01()函数 def fun01(): print('--- fun01函数 start ---') print(10 // 2) print('--- fun01函数 end ---') # 2. 定义fun02()函数, 调用fun01()函数 def fun02(): print('--- fun02函数 start ---') # 调用fun01()函数 fun01() print('--- fun02函数 end ---') # 3. 在main函数中, 测试上述的内容. if __name__ == '__main__': fun02() print('看看我执行了吗!')