约束和异常处理
本节主要内容:
1.类的约束
2.异常处理
3.自定义异常
4.日志
一.类的约束
⾸先, 你要清楚. 约束是对类的约束. 比如. 现在. 你是一个项⽬经理. 然后呢. 你给手下 的人分活. 张三, 你处理一下普通用户登录,
李四, 你处理一下会员登录, 王五, 你处理一下管理员登录. 那这个时候呢. 他们就开始分别取写他们的功能了了. 但是呢. 你要知道,
程序员不一定会有那么好的默契. 很有可能三个人会写完全三个不同的方法. 就比如这样:
# 贴吧 # 项目经理(级别高一点儿) class base: def login(self): # 强制子类做xxxx事 raise notimplementederror("子类没有实现该方法") # 报错. 抛异常 # 1. 普通账号 --> 张三 class normal(base): def login(self): print("普通账号的登录") # 2. 吧务 - > 李四 class member(base): def login(self): print("吧务的登录") # 3. 百度员工 -> 王五 class admin(base): def login(self): # 方法的覆盖和重写 print("管理员的登录") # 项目经理 def wodetian(obj): obj.login() n = normal() wodetian(n) m = member() wodetian(m) a = admin() wodetian(a)
然后呢, 他们把这样的代码交给你了. 你看一眼. 张三和王五还算ok 这个李四写的是 什么鬼? denglu.......难受不.
但是好歹能用. 还能凑合. 但是这时. 你这边要使用了. 问题就来了.
对于张三和王五的代码. 没有问题. 但是李四的. 你是不是调用不了. 那如何避免这样的 问题呢? 我们要约束程序的
结构. 也就是说. 在分配任务之前就应该把功能定义好. 然后分别交给底下的程序员来完成相应的功能.
约束的作用:规范代码,约束是对类的约束
在python中有两种办法解决这样的问题:
1.提取父类,然后在父类中定义好办法.在这个方法中什么都不用干,就抛出一个异常就可以了,这样所有的子类就必须重写这个方法.
否则,访问的时候就会报错.
2.使用元类来描述父类.在元类中给出一个抽象方法.这样子类就不得不给出抽象方法的具体实现.也可以起到约束的效果.
首先,我们先看第一张解决方案:首先,提取一个父类,在父类中给出一个方法,并且在方法中不给出任何代码,直接抛出异常.
class base: def login(self): raise notimplementederror class normal(base): def login(self): print("普通用户登录") class member(base): def login(self): print("会员登录") class admin(base): def denglu(self): print("管理员登录") #项目经理写的总入口 def login(obj): print("准备验证码...") obj.login() print("进入主页...") n = normal() m = member() a = admin() login(n) login(m) login(a) #报错
在执行到login(a)的时候程序会报错. 原因是, 此时访问的login()是父类中的方法. 但是父类中的方法会抛出一个异常. 所以报错.
这样程序员就不得不写login方法了. 从而对子类进行了相应的约束.
在本示例中. 要注意. 我们抛出的是exception异常. 而exception是所有异常的根. 我们无法通过这个异常来判断出程序是因为什么
报的错. 所以. 最好是换一个比较专业的错误信息. 最好是换成notimplementerror. 其含义是. "没有实现的错误". 这样程序员或者项
⽬经理理可以一目了然的知道是什么错了. 就好比. 你犯错了. 我就告诉你犯错了. 你也不知道哪里错了. 这时我告诉你, 你xxx错了.
你改也好改不是?
第二套方案: 写抽象类和抽象方法. 这种方案相对来说比上一个麻烦一些. 需要给大家先引入一个抽象的概念我们如果写了一个方法,
不知道方法的内部应该到底写什么.那这个方法其实就应该是一个抽象的方法.如果一个类中包含抽象方法,那么这个类一定是抽象类
抽象类是不能有实例对象的.创建对象的时候会报错.
在python中编写一个抽象类需要引入abc模块中的abcmeta和abstractmethod这两个内容.
from abc import abcmeta,abstractmethod # 类中包含了抽象方法,那此时这个类就是个抽象类.注意:抽象类可以有普通方法 class animal(metaclass=abcmeta): @abstractmethod def chi(self): pass # 抽象类不能创建对象 class dog(animal): # 子类必须实现父类中的抽象方法,否则子类也是抽象类 def chi(self): print("躺着吃") class cat(animal): def he(self): print("喝水") d = dog() d.chi() c = cat() c.he()
通过代码我们能发现,这里的父类animal对子类dog和cat进行了约束
总结:约束.其实就是父类对子类进行约束.子类必须要写xxx方法.在python约束的方式有两种:
1.使用抽象类和抽象方法,由于该方案来源是java和c#.所以使用评率还是很少的
2.使用人为抛出异常的方案,并且尽量抛出的是notimplementerror.这样比较专业,而且错误比较明确.(推荐)
二.异常处理
异常:程序在运行过程中产生的错误.
def cul(a,b): return a/b ret = cul(10/0) print(ret) 结果: traceback (most recent call last): file "d:/python课件及作业/约束/121.py", line 4, in <module> ret = cul(10/0) zerodivisionerror: division by zero
什么是错误,除法中除数不能是0.那如果真的出了这个错.我们不可能吧一堆错误信息抛给客户,那该如何处理?
def cul(a,b): return a/b try: ret = cul(10/0) print(ret) except exception as e: print("除数不能是0")
try..except的作用就是当程序运行时出现了错误,就执行except后面的代码.在和这个过程中.当代码出现错误的时候,
系统会产生⼀个异常对象. 然后这个异常会向外抛. 被except拦截. 并把接收到的异常对象赋值给e. 那这里的e就是
异常对象. 那这里的 exception是什么? exception是所有异常的基类, 也就是异常的根. 换句话说. 所有的错误都是
exception的子类对象. 我们看到的zerodivisionerror 其实就是exception的子类. 那这样 写好像有点儿问题. exception
表示所有的错误. 太笼统了了. 所有的错误都会被认为是exception. 当程序中出现多种错误的时候, 就不好分类了了, 最
好是出什么异常就⽤用什么来处理. 这样就更加合理了. 所以在try...execpt语句中. 还可以写更多的except.
完整的异常处理写法(语法):
try:
"""操作"""
except exception as e:
"""异常的父类,可以捕获异常"""
else:
"""保护不抛出异常的代码,当try中无异常的时候执行"""
finally:
"""最后要执行的"""
解读:程序先执行操作,然后如果出错了会走except中的代码.如果不出错,执行else中的代码.不论出不出错,最后都要
执行finally中的语句,一般我们用try...except就够用了.顶多加上finally.finally一般用来作为收尾工作.
以上是处理异常,我们在执行代码的过程中如果出现了一些条件上的不对等.根本不符合我的代码逻辑.比如,参数.我要求
传递的是一个数字,而客户非得传递一个字符串.那我们该如何处理来通知客户呢?
方案一:直接返回即可.
方案二:抛出一个异常.
那如何抛出异常呢?我们要用到关键字raise
def add(a,b): """ 传递两个整数,求和 :param a: :param b: :return: """ if not type(a) == int or not type(b) == int: #当程序运行到这句话的时候,正割函数的调用就会被中断,并向外抛出一个异常 raise exception("不是整数,无法求和") return a + b # 如果调用方不处理异常,那产生的错误将会继续向外抛,最后就抛给了用户 # 如果调用方处理了异常. 那么错误就不会丢给用户. 程序也能正常运行 try: add("胡辣汤",1) except exception as e:
当程序运行到raise. 程序会被中断. 并实例化后面的异常对象. 抛给调用方. 如果调用方不处理. 则会把错误继续向上抛出. 最终抛给⽤用户.
如果调用方处理了异常. 那程序可以正常的进行执行.
三.自定义异常
自定义异常:非常简单,只要你的类继承了exception类,那你的类就是一个异常类.
import traceback class gendererror(exception): pass class person: def __init__(self,name,gender): self.name = name self.gender = gender def goto_wc(self): if self.gender == "男": print("进来吧") else: raise gendererror("错了,不可以进来") try: p1 = person("andy","男") p1.goto_wc() p2 = person("amy","女") p2.goto_wc() except gendererror as e: val = traceback.format_exc() print("你不是男的,别来啊") print(val) except exception as e: print("其他错误)
结果:
进来吧
你不是男的,别来啊
traceback (most recent call last):
file "d:/python课件及作业/约束/约束.py", line 83, in <module>
p2.goto_wc()
file "d:/python课件及作业/约束/约束.py", line 77, in goto_wc
raise gendererror("错了,不可以进来")
gendererror: 错了,不可以进来
我们在调试的时候最好是能看到错误院子哪里,那怎么办?
上面的代码引入了另一个模块traceback,这个模块可以获取到我们每个方法的调用信息.又被称为堆栈信息,
这个信息对我们拍错是很有帮助的
四.日志
在编写任何一款软件的时候,都会出现各种各样的问题或者bug,这些问题或者bug一般都会在测试的时候处理掉.
但是多多少少的都会出现一些意想不到的异常或者错误.那这个时候,我们是不知道哪里出现了问题.因为很多都
不是必现的bug.如果是必现的,测试的时候肯定能测出来.最头疼的就是这种不必现的bug.自己运行没有问题,但
是到客户那一用就出问题.那怎么办?我们需要给软件准备一套日志系统.当出现任何错误的时候.我们都可以去日
志系统里去查看.看哪里出了问题.这样解决问题和bug的时候就多了一个帮手.那如何在python中创建这个日志系
统呢?很简单:
1.导入logging模块.
2.简单配置一下logging
3.出现异常的时候(except).向日志里写错误信息.
import logging #filename:文件名 # format:数据的格式化输出.最终在日志文件中的样子 # 时间-名称-级别-模块: 错误信息 # datefmt:时间的格式 # level:错误的级别权重,当错误的级别权重大于等于leval的时候才会写入文件 logging.basicconfig(filename="x1.log",format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:' '%(message)s',datefmt='%y-%m-%d %h:%m:%s', level=10) #当前配置表示10以上的分数会被写入日志文件 # critical = 50 # fatal = critical # error = 40 # warning = 30 # warn = warning # info = 20 # debug = 10 # notest = 0 logging.critical("我是critical") # 50分.最贵的 logging.error("我是error") # 40分 logging.warning("我是warning") # 警告 30分 logging.info("我是基本信息") # 20 logging.debug("我是测试") # 10 logging.log(2,"我是自定义") # 自定义,看着给分
在上面这个模板的基础上做个简单的测试,应用下
import traceback class jackerror(exception): pass for i in range(10): try: if i % 3 == 0: raise filenotfounderror("文件不在啊") if i % 3 == 1: raise keyerror("键错了") if i % 3 == 2: raise jackerror except filenotfounderror: val = traceback.format_exc() logging.error(val) except keyerror: val = traceback.format_exc() logging.error(val) except jackerror: val = traceback.format_exc() logging.error(val) except exception: val = traceback.format_exc() logging.error(val)
最后,如果你系统中想要把日志文件分开.比如,一个大项目,有两个子系统,那两个子系统要分开记录日志,方便调试.
那怎么办呢?注意:用上面的basicconfig是搞不定的,我们要借助文件助手(filehandler),来帮我们完成日志的分开记录:
import logging # 创建一个操作日志的对象logger(依赖filehandler) file_handler = logging.filehandler("l1.log", "a", encoding="utf-8") file_handler.setformatter(logging.formatter(fmt="%(asctime)s - %(name)s - % (levelname)s -%(module)s:%(message)s")) logger1 = logging.logger("日志1",level=logging.error) logger1.addhandler(file_handler) logger1.error("我是a系统") # 再创建一个操作日志的对象logger(依赖filehandler) file_handler = logging.filehandler("l2.log", "a", encoding="utf-8") file_handler.setformatter(logging.formatter(fmt="%(asctime)s - %(name)s - % (levelname)s -%(module)s:%(message)s")) logger2 = logging.logger("日志2",level=logging.error) logger2.addhandler(file_handler)
上一篇: 标点符号、运算符的幽默对话
下一篇: 幽默的恋爱男女