类的约束 和 异常处理
Posted wenqi2121
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了类的约束 和 异常处理相关的知识,希望对你有一定的参考价值。
本节主要内容:
1.类的约束
2.异常处理
3.自定义异常
4.MD5 加密
5.日志
一 类的约束
首先,你要清楚,约束是对类的越是,比如,你是一个项目经理,然后呢,你给手下的人分活,张三,你处理一下普通用户登录,李四,你处理一下会员登录,王五,你处理一下管理员登录,那这个时候呢,他们就开始分别去写他们的功能了,但是呢,你要知道,程序员不一定会有那么好的默契,很有可能三个程序员会写三个完全不同的方法,比如这样:
1 class Normal: # 张三,普通会员登录 2 def login(self): 3 pass 4 5 class Member: # 李四,会员登录 6 def denglu(self): 7 pass 8 9 class Admin: # 王五,管理员登录 10 def login(self): 11 pass
然后呢,他们把代码交给你了,你看了一眼,张三和王五还算ok,这个李四写的什么鬼啊?denglu....
难受不??但是好歹能用,还能凑活,但是这时你这边要使用了,问题就来了
1 # 项目经理的入口 2 3 def login(obj): 4 print("准备验证码.....") 5 obj.login() 6 print("进入主页........")
对于张三和王五的代码,没有问题,但是李四,你是不是调用不了,那如何避免这样的问题呢?我们要约束程序的结构,也就是说,在分配任务之前就应该把功能定义好,然后分别交给下面的程序员来完成相应的功能
在python 中有两种办法来解决这样的问题
1.提取父类,然后在父类中定义好方法,在这个方法中什么都不用干,就抛一个异常就可以了,这样所有的子类就必须重写这个方法,否则,访问的时候就会报错
2.使用元类来描述父类,在元类中给出一个抽象方法,这样子类就不得不给出抽象方法的具体实现,也可以得到约束的效果
首先,我们先看一种解决方案,首先,提取一个父类,在父类中给出一个方法,并且在方法中不给出任何代码,直接抛异常
1 class Base: 2 def login(self): 3 raise Exception("你没有实现方法login()") 4 class Normal(Base): 5 def login(self): 6 pass 7 8 class Member(Base): 9 def denglu(self): 10 pass 11 12 class Admin(Base): 13 def login(self): 14 pass 15 16 # 项目经理的入口 17 def login(obj): 18 print("准备验证码.....") 19 obj.login() 20 print("进入主页........") 21 22 n = Normal() 23 m = Member() 24 a = Admin() 25 login(n) 26 login(m) # 报错 Exception: 你没有实现方法login() 27 login(a)
在执行 login(m)的时候程序会报错,原因是,此时访问的 login() 是父类总的方法,但是富尅 的方法会抛出一个异常,所以报错,这样程序员就不得不写 login方法了,从而对子类进行了相应的约束
在本例中,要注意,我们抛出的是 Exception 异常,而 Exception 是所有异常的根,我们无法通过这个异常来判断出程序 是因为什么报的错,所以,最好是换一个 专业的错误信息,最好是换成 NotlmplementError
其含义是"没有实现的错误" 这样程序员 或者 项目经理 可以一目了然的知道是什么错了,就好比,你犯什么错了,你也不知道哪里错了,这时我告诉你,你xx 错了,你改也知道改哪不是?
第二套方案,写抽象类 和抽象方法,这种方案相对于上一种方案来说麻烦一些,需要先引入一个抽象的概念,什么是抽象呢?这里动物的吃就是一个抽象的概念,只是一个动作的概念,没有具体实现,这就是抽象的动作,换句话说,我们如果写一个方法,不知道方法的内部应该到底怎么写,那这个方法其实就应该是一个抽象的方法,如果一个类中包含抽象方法,那么这个类一定是一个抽象类,抽象类是不能有实例的,比如,你看看一些抽象派的画作,在现实中是不存在的,也就无法建立实例对象与之相对应,所以抽象类无法创建对象,创建对象的时候会报错.
在 python 中编写一个抽象类比较麻烦,需要 引入 abc 模块中的 ABCMeta 和 abstractmethod 这两个内容,来看一个例子
1 from abc import ABCMeta,abstractmethod 2 3 # 类中包含了抽象方法,那此时这个类就是个抽象类,注意:抽象类可以有普通方法 4 class IGame(metaclass=ABCMeta): 5 # 一个游戏到底怎么玩儿?你能形容?路程能一样么? 6 @abstractmethod 7 def play(self): 8 pass 9 10 def turn_off(self): 11 print("破游戏不玩了,脱坑了") 12 13 class DNFGame(IGame): 14 # 子类必须实现父类中的抽象方法,否则子类也是 抽象类 15 16 def play(self): 17 print("dnf 的玩儿法") 18 19 # g = IGame() # 抽象类不能创建对象 20 21 dg = DNFGame() 22 dg.play()
通过代码我们发现这里的 IGame 对 DNFGame 进行了约束,换句话说,父类对子类进行了约束
from abc import ABCMeta,abstractmethod class Base(metaclass=ABCMeta): @abstractmethod def login(self): pass class Normal(Base): def login(self): pass class Member(Base): def denglu(self): # 这个没用了 pass def login(self): # 子类对 父类 进行 实现 pass class Admin(Base): def login(self): pass # 项目经理入口 def func(obj): print("准备验证码...") obj.login() print("进入主页.....") n = Normal() m = Member() a = Admin() func(n) func(m) func(a)
总结:约束,其实就是父类对子类进行的约束,子类必须要按照父类的要求 写 xx方法,在python中 约束的方式有两种:
1.使用抽象类和抽象方法,由于该方案来源是 java 和 c# ,所以使用频率还是很少的
2.使用人为抛出异常的方案,并且尽量抛出的是 NotlmplementError,这样比较专业,而且错误比较明显
二 异常处理
首先我们说一下,什么是异常, 异常 是程序在运行过程中产生的错误,就好比,你在回家的路上突然天塌了,那这个属于一个异常,总之就是不正常,那如果程序出现了异常,怎么处理呢?
我们先制造一个错误,来看看异常长什么样
def chu(a,b): return a/b ret = chu(10,0) print(ret) # 结果: # ZeroDivisionError: division by zero
什么错误呢?除法中除数不能为0,那如果真的出了这个错,你把这一对错误抛给使用者嘛? 肯定不能啊
那如何处理呢???
def chu(a,b): return a/b try: ret = chu(10,0) print(ret) except Exception as e: print(e) print("除数不能为0") # 结果: # division by zero # 除数不能为0
那么 try ....except 是什么意思呢?尝试着运行 xxxx代码,出现了错误,就执行 except 后面的代码,在这个过程中,当代码出现错误的时候,系统会产生一个异常对象,然后这个异常 会向外抛,被 except 拦截, 并把接收到的异常对象 赋值给 e,那这里的 e 就是异常对象, 那这里的 Exception 是什么呢? Exception 是所有的异常的 基类(父类) ,也就是异常的根,换句话说,所有的错误都是 Exception 的子类,那这样写 好像有点问题啊, Exception 表示所有的错误,太笼统了,所有的错误都会被认为是 Exception,当程序出 多种错误的时候,就不好分类了,组好事什么异常就用什么处理,这样就更加合理了,所以在 try...except 语句汇总,还可以写入更多的 except
try: print("各种操作...") except ZeroDivisionError as e: print("除数不能为0") except FileNotFoundError as e: print("文件不存在...") except Exception as e: print("其他错误")
此时,程序运行过程中,如果出现了ZeroDivisionError 就会被第一个 except 捕获,如果出现了FileNotFountError 就会被第二个 except 捕获,如果都不是这链各个异常,那就会被最后的 Exception 捕获,总之最后的 Exception 就是我们处理 异常的最后一道防卫,这是我们 最常用的一套写法, 接下来看一套最完整的 异常处理写法(语法):
try: """ 操作""" except Exception as e: """ 异常的父类,可以捕获所有的异常...""" else: """保护不抛出异常的代码,当try 中 无异常的时候执行...""" finally: """最后总是要执行我...."""
解读:程序先执行操作,然后如果出错了会走 except的代码,如果不出错,这行else 中的代码,不论出不出错,最后都要执行 finally 中的 语句, 一般我们 用 try...except 就够了,顶多加上 finally ,finally 一般用来作为收尾工作
上面是处理异常,我们在执行代码的过程中如果出现了一些条件上的不对等,根本不符合我的代码逻辑,比如,参数,我要求你传递一个数字,你非传递一个字符串,那 "对不起,我是 警察" 哈哈哈....... 这样的我是没办法处理的,那如何通知你呢? 两个 方案:
1.方案1: 直接返回即可,我不管你
2.方案2: 抛出一个异常,告诉你,我不好惹,乖乖的听话
第一种方案是我们之前写代码经常用到的方案,但这种方案并不够好,无法起到警示作用,所以,以后的代码中出现了类似的问题,直接抛出一个错误,那怎么抛呢? 我们要用到 raise 关键字
def add(a,b): """ 给我传递两个参数,我帮你计算这两个数的和 :param a: :param b: :return: """ if not type(a) == int and type(b) == int: """ 当程序运行到这句话的时候,整个函数的调用会中断,抛出一个异常""" raise Exception("不是整数,臣妾做不到啊") return a + b # 如果调用方不处理异常,那产生的错误将会继续往外抛,最后就抛给了使用者 # add("你好,我叫 张曼玉") # 如果 调用方吹了异常,那么错误就不会 抛给使用者,程序也能正常运行 try: add("我是至尊宝","你是紫霞仙子嘛?") except Exception as e: print("报错了,你自己看着办吧")
当程序运行到 raise 的时候, 程序会被中断,并实例化后面的异常对象,抛给调用方,如果调用方不处理,则会把错误继续向上抛出,最后抛给 使用者,如果调用方处理了异常,那程序可以正常运行了
说了这么多,异常也知道如何抛出 和 处理了,但是现在我们用的都是 python 自带 的异常,如果有一天,你自己写的代码中 出现了 一个 无法用现有的 异常来解决,那怎么办? 试试,能不能 ,自定义一个??/
其实 自定义一个 也很简单,只要你的类继承 了 Exception 类,那你的类就是一个 异常类,就这么简单.
# 继承 Exception ,那这个类就是一个 异常类 class GenderError(Exception): pass class Person: def __init__(self,name,gender): self.name = name self.gender = gender def men_bathroom(person): if person.gender != "男": raise GenderError("性别不对,这里是男澡堂") p1 = Person("哈哈哥","男") p2 = Person("嘻嘻姐","我不知道") # men_bathroom(p1) # men_bathroom(p2) # 会抛出一个异常 GenderError # 处理异常 try: men_bathroom(p1) men_bathroom(p2) except GenderError as e: print(e) # 性别不对,这里是男澡堂 except Exception as e: print("反正报错了")
搞定了,但是,如果是真的报错了,我们在测试的时候,最好是能看到错误源于哪里,怎么办呢? 需要引入一个模块, traceback,这个模块可以获取到我们每个方法的调用信息,有被称为堆栈信息,这个信息对我们排错很有帮助的
import traceback # 继承 Exception,那这个类 就是一个 异常类 class GenderError(Exception): pass class Person: def __init__(self,name,gender): self.name = name self.gender = gender def men_bathroom(person): if person.gender != "男": raise GenderError("性别不对,别进了") p1 = Person("哈哈哥","男") p2 = Person("嘻嘻姐","不知道哎") try: men_bathroom(p1) men_bathroom(p2) except GenderError as e: val = traceback.format_exc() # 获取到堆栈信息 print(e) # 性别不对,别进了 print(val) except Exception as e: print("反正错了") # 结果 # 性别不对,别进了 # raise GenderError("性别不对,别进了") # GenderError: 性别不对,别进了
搞定了,这样我们就能收放自如了,当测试代码的时候吧 堆栈信息打印出来,但是当到了线上的生产环境的时候把这个堆栈去掉即可
四 MD5加密
想一个事情. 你在银行取钱或者办卡的时候. 我们都要输入密码. 那这个密码如果就按照我们输入的那样去存储. 是不是很不安全啊. 如果某一个程序员进入到了了银行的数据库. 而银行的数据库又存的都是明文(不加密的密码)密码. 这时, 整个银行的账户里的信息都是非常不安全的. 那怎么办才安全呢? 给密码加密. 并且是不可逆的加密算法. 这样. 即使获取到了银行的账户和密码信息. 对于黑客而言都无法进行破解. 那我们的账号就相对安全了了很多. 那怎么加密呢? 最常见的就是用MD5算法.
MD5是一种不可逆的加密算法. 它是可靠的. 并且安全的. 在python中我们不需要手写
这一套算法. 只需要引入?个叫 hashlib 的模块就能搞定MD5的加密工作
import hashlib obj = hashlib.md5() obj.update("哈哈哥".encode("utf-8")) # 加密的必须是字节 miwen = obj.hexdigest() print(miwen) # 如果担心被破解,也就是 撞库,可以加盐,对没错,就是 加盐 import hashlib obj = hashlib.md5(b"saddfgflhmdlfmdas") # 加盐 obj.update("alex".encode("utf-8")) miwen = obj.hexdigest() print(miwen) # 现在可以了,不会再担心 撞库了
那 MD5 怎么用呢??
import hashlib def my_md5(s): obj = hashlib.md5() obj.update(s.encode("utf-8")) miwen = obj.hexdigest() return miwen username = input("输入用户名:") password = input("输入密码:") # 数据存储 的时候: # username:my_md5(password) # 假设现在的用户名 和 密码分别是 # hahage:b3ed7cbc208610c252e2ea825c5f31e5 # 123456:e10adc3949ba59abbe56e057f20f883e # print(my_md5(username)) # print(my_md5(password)) # 用户登录 if my_md5(username) == "b3ed7cbc208610c252e2ea825c5f31e5" and my_md5(password) == "e10adc3949ba59abbe56e057f20f883e": print("成功") else: print("失败")
所以,以后存密码就不要存明文了,要存密文,安全,并且,这里的加盐不能改来改去的 ,否则,整套密码就都乱了
以上是关于类的约束 和 异常处理的主要内容,如果未能解决你的问题,请参考以下文章