Python | 4 异常、模块与包
本章内容:Python 中的异常(bug)、模块(module)与包(package)。
所有相关代码可在https://github.com/hzyao/Python-Cookbook
进行查看。边学习边实操,及时挖掘真美妙!搭配食用或许口味更佳哟!
异常、模块与包
1 异常
当检测到一个错误时,Python 解释器就无法运行了,且会出现一些错误提示,这就是所谓的“异常”,也就是我们常说的“bug”。
异常:程序运行过程中出现了错误。
bug:也就是异常,由于历史上小虫子导致计算失灵的案例,此后延续应用至今。
bug 例子自己写!相信大家见得一定不少哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈!!!
1.1 异常的捕获方法
世界上没有完美的程序,任何程序在运行的过程中都有可能出现异常,也就是出现 bug 导致程序无法完美运行下去。
咱们要做的,不是力求程序完美运行,而是在力所能及的范围内对可能出现的 bug,进行提前准备与处理。
这种行为我们称为异常处理(捕获异常)。
当我们的程序遇到了 bug,那么接下来有两种情况:
- 整个程序因为一个 bug 停止运行;
- 对 bug 进行提醒,整个程序继续运行。
显然在之前的学习中,我们所有的程序遇到 bug 就会出现 1 的这种情况,也就是整个程序直接奔溃;但是在真实工作中,我们肯定不能因为一个小的 bug 就让整个程序全部奔溃,也就是我们希望的是达到 2 的这种情况。那么这里我们就需要使用到捕获异常。
捕获异常的作用在于提前假设某处会出现异常,做好提前准备,当真的出现异常的时候,可以提供后续解决方案。
概述:
try:
可能发生异常的语句
except:
出现异常的准备手段
else:
未出现异常时要做的事情
finally:
不管出不出现异常都会做的事情
详解:
-
捕获常规异常
语法:
try: 可能发生错误的代码 except: 出现异常执行的代码
例子:
# 捕获常规异常 try: f = open("./data/fake.txt", "r", encoding="UTF-8") except: print("bug") f = open("./data/fake.txt", "w", encoding="UTF-8")
-
捕获指定异常
语法:
try: 可能发生错误的代码 except TypeError as 别名: 出现异常执行的代码
注:
- 如果尝试执行的代码异常和要捕获的异常类型不一致,则无法捕获异常;
- 一般
try
下方只放一行尝试执行的代码; - 别名中记录着异常的具体信息。
例子:
# 捕获指定异常 try: print(xmy) except NameError as e: print("bug") print(e)
# 无法捕获其他类型异常,直接报错 try: 1 / 0 except NameError as e: print("bug") print(e) --------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) /tmp/ipykernel_9715/2479312728.py in 1 # 无法捕获其他类型异常,直接报错 2 try: ----> 3 1 / 0 4 except NameError as e: 5 print("bug") ZeroDivisionError: division by zero
-
捕获多个异常
当捕获多个异常时,可把要捕获的异常类型名放到
except
后,使用元组的方式进行书写。# 捕获多个异常 try: 1 / 0 except (NameError, ZeroDivisionError) as e: print("bug") print(e)
-
捕获所有异常
前面 捕获常规异常 部分的语法未进行限定,可以捕获所有异常,还有如下的顶级方法:
# 捕获所有异常 try: 1 / 0 # print(name) except Exception as e: print("bug") print(e)
-
异常 else
else
表示如果没有异常要执行的代码。# 异常else try: print("name") except Exception as e: print("sad") else: print("happy")
-
异常 finally
finally
表示无论是否异常都要执行的代码,例如关闭文件。# 异常finally try: print("name") except Exception as e: print("sad") else: print("happy") finally: f.close()
1.2 异常的传递
异常具有传递性。注:当所有函数都没有捕获异常的时候,程序就会报错。
# 异常的传递性演示
## 定义一个有异常的函数
def func():
print("func1 start")
num = 1 / 0 # 异常
print("func1 over")
## 定义一个无异常的函数,调用上面的函数
def func():
print("func2 start")
func1()
print("func2 over")
## 定义一个主函数,调用上面的函数
def main():
try:
func2()
except Exception as e:
print("bug")
print(e)
main()
2 模块
2.1 什么是模块
Python 模块(module):是一个 Python 文件,以 .py 结尾,内部包含函数、类、变量和可执行的代码等。通俗来讲,模块就是一个 Python 文件,里面有类、函数、变量等,我们可以拿过来用(导入模块去使用)。
作用:Python 中有很多各种不同的模块,每一个模块都可以帮助我们快速的实现一些功能,比如实现和时间相关的功能就可以使用 time 模块。我们可以认为一个模块就是一个工具包,每一个工具包中都有各种不同的工具供我们使用进而实现各种不同的功能。
2.2 模块的导入方式
模块在使用前需要先导入,语法如下:
[form 模块名] import [模块 | 类 | 变量 | 函数| *] [as 别名]
常见组合形式如下:
import 模块名
from 模块名 import 类、变量、方法等
from 模块名 import *
import 模块名 as 别名
from 模块名 import 功能名 as 别名
-
import 模块名
# 模块导入 import time print("hello") time.sleep(3) # 通过 . 就可以使用模块内部的全部功能(类、函数、变量) print("hi")
-
from 模块名 import 功能名
# 导入模块中的功能 from time import sleep print("hello") sleep(3) print("hi")
-
from 模块名 import *
# 使用 * 导入模块中的全部功能 from time import * print("hello") sleep(3) # 与上述直接导入模块作用相同,但是使用模块中函数时可直接使用,不需要用 . print("hi")
-
as 定义别名
# 模块定义别名 import time as t t.sleep(2) # 功能定义别名 from time import sleep as sl sl(2)
注:
- from可以省略,直接import即可
- as别名可以省略
- 通过.来确定层级关系
- 模块的导入一般写在代码文件的开头位置
2.3 自定义模块
Python 中已经帮我们实现了很多的模块,不过有时候你可能会需要一些个性化的模块,我们可以通过自定义模块去实现,也就是说你可以自己制作一个模块。导入方法同 Python 内置模块相同。
# 导入自定义模块(先自己去构建一个!俺的叫 *mymodule*)
## 我的模块
def test(a, b):
print(a + b)
## 导入我的模块
import mymodule
mymodule.test(3, 6)
注:当导入多个模块且不同模块内有同名功能时,则后导入的模块功能会覆盖先导入的。
在实际开发中,当我们编写完一个模块后,为了让模块能够在项目中达到想要的效果,我们可能会自行在 .py 文件中添加测试代码(test()
)。这种情况下,无论是当前文件还是其他已经导入了该模块的文件,在运行的时候都会自动执行test
函数的调用。我们可以在 .py 文件中使用__main__
来解决这个问题。
if __name__ == "__main__"
表示只有当程序是直接执行的才会进入if
内部,如果是被导入,则if
无法进入。
# 我的module内容
def test(a, b):
print(a + b)
# 只在当前文件中调用该函数,其他文件导入此模块则不会调用该函数
if __name__ == "__main__":
test(1, 1)
如果一个模块文件中有__all__
变量,当使用from xxx import *
导入时,只能导入这个列表中的元素。
# 自定义模块内容
__all__ = ["test"] # 这样在其他文件中以 * 导入时,就只能使用test函数
def test(a, b):
print(a + b)
def test_b(a, b):
print(a - b)
if __name__ == "__main__":
test(1, 1)
3 包
3.1 什么是 Python 包
从物理上看,包就是一个文件夹,在该文件夹下包含了一个__init__.py
文件,该文件夹可用于存放多个模块文件(代码文件);从逻辑上看,包的本质依然是模块。
包的作用:当我们的模块文件越来越多时,包可以帮助我们管理这些模块。包的作用就是包含多个模块,将其归为一类,方便使用,但包的本质依然是模块。
3.2 自定义 Python 包的使用
首先,自定义一个包,还是蛮简单的,创建一个文件夹,在文件夹下创建__init__.py
文件(有这个文件就表明这个文件夹是Python包,而不是普通的文件夹,pycharm支持自动创建),然后创建你想要的模块就好啦!就像这样:
导入自定义包的方法同导入包相同,如下所示:
import 包名.模块名
包名.模块名.目标
# mypkg_module_1.py 中的内容,mypkg_module_2.py 中同,仅把1改为2
"""
module 1
"""
def info_print1():
print("modele 1")
# 导入自定义包,与导入包方式相同
## 第一种写法
import mypkg.mypkg_module_1
import mypkg.mypkg_module_2
mypkg.mypkg_module_1.info_print1()
mypkg.mypkg_module_2.info_print2()
## 第二种写法
from mypkg import mypkg_module_1
from mypkg import mypkg_module_2
mypkg_module_1.info_print1()
mypkg_module_2.info_print2()
## 第三种写法
from mypkg.mypkg_module_1 import info_print1
from mypkg.mypkg_module_2 import info_print2
info_print1()
info_print2()
导入包中,也有和之前在模块部分讲到的__all__
变量,和在模块中一样拥有控制import *
导入内容的功能。要注意的是,必须在__init__.py
文件中添加__all__ = []
,控制允许导入的模块列表。
from 包名 import *
模块名.目标
# __init__.py 中的内容,[]中是包中可以用的模块的名字
__all__ = ['mypkg_module_1']
# 导入包2.0
from mypkg import *
mypkg_module_1.info_print1()
mypkg_module_2.info_print2() # 2未被导入,运行会报错
注:__all__
针对的是from xxx import *
,对import xxx
无效。