类的约束 和 异常处理

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
View Code

 

  然后呢,他们把代码交给你了,你看了一眼,张三和王五还算ok,这个李四写的什么鬼啊?denglu....

难受不??但是好歹能用,还能凑活,但是这时你这边要使用了,问题就来了

技术分享图片
1 # 项目经理的入口
2 
3 def login(obj):
4     print("准备验证码.....")
5     obj.login()
6     print("进入主页........")
View Code

  对于张三和王五的代码,没有问题,但是李四,你是不是调用不了,那如何避免这样的问题呢?我们要约束程序的结构,也就是说,在分配任务之前就应该把功能定义好,然后分别交给下面的程序员来完成相应的功能

  在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)
View Code

  在执行 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)
View Code

 

 

  总结:约束,其实就是父类对子类进行的约束,子类必须要按照父类的要求 写 xx方法,在python中 约束的方式有两种:

  1.使用抽象类和抽象方法,由于该方案来源是 java 和 c# ,所以使用频率还是很少的

  2.使用人为抛出异常的方案,并且尽量抛出的是 NotlmplementError,这样比较专业,而且错误比较明显

 

二 异常处理

  首先我们说一下,什么是异常, 异常 是程序在运行过程中产生的错误,就好比,你在回家的路上突然天塌了,那这个属于一个异常,总之就是不正常,那如果程序出现了异常,怎么处理呢?

  我们先制造一个错误,来看看异常长什么样

 

 

技术分享图片
def chu(a,b):
    return a/b

ret = chu(10,0)
print(ret)
# 结果:
# ZeroDivisionError: division by zero
View Code

 

 

   什么错误呢?除法中除数不能为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
View Code

 

 

 

  那么 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("其他错误")
View Code

 

  此时,程序运行过程中,如果出现了ZeroDivisionError 就会被第一个 except 捕获,如果出现了FileNotFountError 就会被第二个 except 捕获,如果都不是这链各个异常,那就会被最后的 Exception  捕获,总之最后的 Exception 就是我们处理 异常的最后一道防卫,这是我们 最常用的一套写法, 接下来看一套最完整的 异常处理写法(语法):

技术分享图片
try:
    """ 操作"""
except Exception as e:
    """ 异常的父类,可以捕获所有的异常..."""
else:
    """保护不抛出异常的代码,当try 中 无异常的时候执行..."""
finally:
    """最后总是要执行我...."""
View Code

  解读:程序先执行操作,然后如果出错了会走 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("报错了,你自己看着办吧")
View Code

  当程序运行到 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("反正报错了")
View Code

    搞定了,但是,如果是真的报错了,我们在测试的时候,最好是能看到错误源于哪里,怎么办呢? 需要引入一个模块, 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: 性别不对,别进了
View Code

  搞定了,这样我们就能收放自如了,当测试代码的时候吧 堆栈信息打印出来,但是当到了线上的生产环境的时候把这个堆栈去掉即可

 

四 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)
# 现在可以了,不会再担心 撞库了
View Code

  那 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("失败")
View Code

  所以,以后存密码就不要存明文了,要存密文,安全,并且,这里的加盐不能改来改去的 ,否则,整套密码就都乱了

 




以上是关于类的约束 和 异常处理的主要内容,如果未能解决你的问题,请参考以下文章

类的约束 和 异常处理

python之路--类的约束, 异常处理, MD5, 日志处理

Python-约束和异常处理

20 约束 异常处理 MD5 日志处理

python 约束与异常处理

Python基础20_类的约束,异常处理,MD5加密,日志