21 异常处理
21.1 异常的含义和处理
21.1.1 什么是异常
非正常,没有达到预期的目标。
异常是一个事件,异常事件在程序运行过程中出现,会影响程序的正常执行。
异常分两种:
1.语法错误导致的异常
2.逻辑错误导致的异常
在程序无法正常运行处理时,会出现一个异常,在python中异常是一个对象,表示一个错误。
标准异常类 表格:https://www.runoob.com/python/python-exceptions.html
21.1.2 如何处理异常
1.如果错误发生的情况是可以预知的,那么就可以使用流程控制进行预防处理
比如:两个数字的运算,其中一个不是数字,运算就会出错,这时可以去判断来预防
n2 = '3'
if isinstance(n2,int):
res = 10 + n2
print(res)
2.如果错误发生的条件不可预知,可以使用 try:... except...在错误发生时进行处理
语法:
try:
可能发生异常错误的代码
except:
如果发生异常则进入 except 代码块进行处理
例子:假设读取的文件不存在,会发生错误,可以使用两种方式进行处理
- 可以在文件读取前先判断当前的文件是否存在
- 使用 try:... except...在错误发生时进行处理
try:
with open('user.txt','r') as fp:
res = fp.read()
print(res)
except:
print('wrong code')
print('continue processing ')
21.2 try:except 用法详解
21.2.1 处理指定的异常类
若引发了非指定的异常,则无法处理
try:
s = 'hello'
int(s) # 引发ValueError
except ValueError as e:
# except IndexError as e: # 非指定异常
print(e,type(e))
* 一个 try 语句可能包含多个except子句,分别来处理不同的特定的异常。最多只有一个分支会被执行。
* 处理程序将只针对对应的 try 子句中的异常进行处理,而不是其他的 try 的处理程序中的异常。
* 一个except子句可以同时处理多个异常,这些异常将被放在一个括号里成为一个元组,例如:
except (RuntimeError, TypeError, NameError):
pass
21.2.2 多分支处理 异常类
不同的异常走向不同的处理
s = 'hello'
try:
int(s) # 引发ValueError
# s[9] # IndexError
except ValueError as e:
print('valueerror',e)
except KeyError as e:
print('keyerror',e)
except IndexError as e:
print('indexerror',e)
21.2.3 通用异常类 Exception
s = 'hello'
try:
int(s)
except IndexError as e:
print('indexerror',e)
except Exception as e:
print('exceptionerror',e)
21.2.4 多分支异常+通用异常类
引发异常后,按照从上往下的顺序去执行对应的异常处理类
s = 'hello'
try:
int(s)
except ValueError as e:
print('valueerror',e)
except KeyError as e:
print('keyerror',e)
except IndexError as e:
print('indexerror',e)
except Exception as e:
print('exceptionerror',e)
21.2.5 try... except... else...
异常时执行except,没异常时执行else
s = 'hello'
try:
int(s)
except ValueError as e:
print('valueerror',e)
except KeyError as e:
print('keyerror',e)
except IndexError as e:
print('indexerror',e)
except Exception as e:
print('exceptionerror',e)
else:
print('try代码块中没有引发异常时,执行')
21.2.6 try... except... else...finally
finally无论是否引发异常,都会执行,通常用于一些清理工作
s = 'hello'
try:
int(s)
print('若前面引发异常,这个代码块不再执行')
except ValueError as e:
print('valueerror',e)
except KeyError as e:
print('keyerror',e)
except IndexError as e:
print('indexerror',e)
except Exception as e:
print('exceptionerror',e)
else:
print('try代码块中没有引发异常时,执行')
finally:
print('无论是否引发异常,都会执行这个代码块')
print('若上面有异常并进行了处理,后面的代码将继续执行')
21.2.7 使用 raise 语句,主动触发(抛出)异常
raise [Exception [, args [, traceback]]]
语句中 Exception 是异常的类型(例如,NameError)参数标准异常中任一种,args 是自己提供的异常参数。
最后一个参数是可选的(在实践中很少使用),如果存在,是跟踪异常对象。
try:
raise Exception('wrong wrong')
except Exception as e:
print(e)
def mye( level ):
if level < 1: # 符合某种条件,产生异常
raise Exception,"Invalid level!"
# 触发异常后,后面的代码就不会再执行
try:
mye(0) # 触发异常
except Exception,err:
print 1,err
else:
print 2
a = int(input('please enter number '))
try:
if a < 30:
raise Exception('wrong variable set',a)
except Exception as e:
print(e) # ('wrong variable set', 2)
else:
print('correct')
finally:
print('move to next part')
最后一个except子句可以忽略异常的名称,它将被当作通配符使用。你可以使用这种方法打印一个错误信息,然后再次把异常抛出。
import sys
try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except OSError as err:
print("OS error: {0}".format(err))
except ValueError:
print("Could not convert data to an integer.")
except:
print("Unexpected error:", sys.exc_info()[0])
raise
try:
runoob()
except AssertionError as error:
print(error)
else:
try:
with open('file.log') as file:
read_data = file.read()
except FileNotFoundError as fnf_error:
print(fnf_error)
finally:
print('这句话,无论异常是否发生都会执行。')
21.2.8 assert 断言
直接判断语句是否成立
断言可以在条件不满足程序运行的情况下直接返回错误,而不必等待程序运行后出现崩溃的情况
assert 1 == 1 # 若成立,则执行后面语句
assert 2 == 1 # 不成立,则报错
import sys
assert ('linux' in sys.platform), "该代码只能在 Linux 下执行"
21.3 自定义异常处理类
当异常出现时,对异常信息进行处理 并写入日志
日志的基本格式:
日期时间, 异常的级别
异常信息:引发的异常类,异常的信息,文件及行号
import traceback 回溯模块 https://docs.python.org/3/library/traceback.html
import logging 日志模块 https://docs.python.org/3/library/logging.html
# try:
# int('aa')
# except:
# # 通过 traceback 模块获取异常信息
# errmsg = traceback.format_exc()
# print(errmsg,repr(errmsg))
# 自定义异常日志处理类
class Myexception():
def __init__(self):
import traceback
import logging
# logging 的基本配置
logging.basicConfig(
filename='error.log', # 日志存放的文件及目录
format='%(asctime)s %(levelname)s \n %(message)s ', # 格式化存储的日志格式
datefmt='%Y-%m-%d %H:%M:%S'
)
# 写入日志
logging.error(traceback.format_exc())
# 使用自定义异常处理类
try:
int('aa')
except:
print('在此处进行异常处理')
Myexception() # 在异常处理的代码块中 调用自定义异常类
print('hmmm...')
22 模块与包
模块
定义的一个python文件,后缀名为.py,这个文件被称为模块
模块中通常会定义一些相似的类,函数等代码的内容,提供给别的程序 引入后使用
系统模块
即一个python程序脚本,专门提供给程序员自己使用,是在安装好python环境时就已存在,需要的时候可以使用import导入到程序中使用
import logging,json,time...
自定义模块
自己创建一个python脚本,定义一些类或方法,供别的脚本导入后使用
22.1 自定义模块的使用
在当前脚本中如果需要使用一些已经定义好的功能时,可以选择对应的模块
- 自定义模块中,通常只是去定义类或函数,变量,等,并不调用
- 若在自定义模块中,想要写一些测试代码,在当前模块作为主程序使用时执行, 作为模块被别的程序导入时,不执行;可以把测试代码写到下面代码块中:
if __name__ == '__main__':
print('这个位置写的代码只有当前脚本被直接运行时触发')
特殊的变量 __name__
__name__
这个变量,在当前脚本作为模块被别的程序导入时,它的值是当前这个模块的名称
在当前脚本作为主程序执行,它的值为__main__
# 使用自定义异常处理模块
import Myex
# 使用模块中定义的类
obj = Myex.Myexception()
print(obj)
# 使用模块中的函数
Myex.func()
# 使用模块中的变量
res = Myex.vari
print(res)
print(Myex.__name__)
# 想使用模块中的内容,除了导入模块,还可以在指定模块中导入指定的内容
from Myex import vari # 导入Myex模块中 vari 变量
from Myex import vari as one # 导入变量,并起别名
print(vari)
print(one)
22.2 自定义包的使用
包
可以理解为一个文件夹,包含多个python文件
包的结构
包(文件夹)
|----__init__.py # 包的标志性文件(可有可无)
|----a.py # a模块(文件)
|----b.py # b模块(文件)
|----...
|----包(文件夹中的文件夹)
|----__init__.py
|----c.py
|----d.py
导入方式
绝对导入
# 0. 如果需要使用包,可以直接导入,不推荐
# 直接把包当作模块导入,可以用的内容是__init__.py文件中定义的
import pp
pp.funcpp()
# 1. 可以导入模块中的所有内容,
# 注意:这个内容是由 __init__.py 文件中定义的
# __all__ = ['filename','']变量指定的
from pp import *
aa.func()
# 2. 导入指定包中的指定模块
from pp import aa,bb
aa.func()
bb.funcc()
# 3. 从指定包中的指定模块 导入指定的内容
from pp.bb import funcc
funcc()
# 4. 从指定包的子包中 导入模块
from pp.ppy import aaa
aaa.func()
# 5. 从指定包的子包的指定模块中 导入指定的内容
from pp.ppy.bbb import funcc
funcc()
22.3 导入方式的分类
22.3.1 绝对导入
使用专门的 [搜索路径] 去查找和导入指定的包或模块
import 模块
import 包
import 包.模块
from 模块 import 内容
from 包 import 模块
from 包.模块 import 内容
22.3.2 相对导入
注意:相对导入只能在非主程序的模块(不需要直接运行的模块文件)中使用,
不要直接运行相对导入的模块的文件
from .包名/模块名 import 模块/内容
from ..包名/模块名 import 模块/内容
. 代表当前, .. 代表上一级
# 假设在当前(子包中的模块文件(pp/ppy/aaa.py))中需要当前包的另一模块 bbb.py
from .bbb import funcbbb
funcbbb()
# from . import bbb
# bbb.funcbbb()
# 在另外一个主运行程序导入方才的子包模块
from pp.ppy import aaa
# 能立即运行刚才的代码
# 假设在当前(子包中的模块文件(pp/ppy/aaa.py))中需要
# 上一级包的另一模块 bbb.py
from ..aa import func
func()
# 在另外一个主运行程序导入方才的子包模块
from pp.ppy import aaa
# 能立即运行刚才的代码
- 了解 搜索路径
导入模块或包时,程序查找的路径
主要搜索路径:
1.当前导入模块的程序所在的文件
2.python的扩展目录中 ../Python3/lib
3.python解释器指定的 第三方模块位置 /lib/sitepackages
# 在当前脚本中 查看 包或模块的 搜索路径
import sys
print(sys.path)
# ['E:\\PycharmProjects\\pythonProject', 'E:\\PycharmProjects\\pythonProject', 'E:\\Programs\\Python\\Python37\\python37.zip',
# 'E:\\Programs\\Python\\Python37\\DLLs', 'E:\\Programs\\Python\\Python37\\lib',
# 'E:\\Programs\\Python\\Python37', 'E:\\Programs\\Python\\Python37\\lib\\site-packages']
# 可以自己定义路径,加入到搜索路径中
sys.path.append('D:/one')
22.4 单入口程序
指整个程序都是经过一个主程序文件运行,其它程序都封装成了包或模块
单入口文件是作为程序直接被运行的唯一文件,其他都是作为模块或包,被导入单入口中去执行
ATM/ |--- main.py # 当前程序的主入口文件,单入口文件 |--- package/ # 主要程序模块包 |--- |---__init__.py # 包的初始化文件 |--- |---View.py # 视图函数模块 |--- |---Controller.py # 控制器模块 |--- |---Card.py # 银行卡模块 |--- |---User.py # 用户模块 |--- databases/ # 数据存储文件夹 |--- |--- user.txt |--- |--- user_id_card.txt
main 是程序的主入口,会被直接作为主程序运行,main.py 文件中必须使用 绝对导入方式