类的约束 和 异常处理

本节主要内容:

  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

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

猜你喜欢

转载自www.cnblogs.com/wenqi2121/p/10325050.html