day26 python约束 自定义异常 hashlib模块 logging模块
一.约束
1.如何定规则: 体现在代码里的规则, 必须要遵守的
父类中编写要约束的函数, raise NotImplementedErrot() 错误: 连参数也可一同约束 (implement 实现)
class BaseMessage: #这个类用于约束派生类, 保证派生类中必须编写send(), 不然执行可能报错
def send(self):
'''
必须继承BaseMessage之后, 必须编写send方法, 用于完成具体的业务逻辑
:return:
'''
raise NotImplementedError(".send() must be overridden")
def func(arg):
'''
报警通知的功能
:param arg:
:return:
'''
arg.send() #如果让三个人分别写一个类, 那么类里面必须要有send方法
class Email(BaseMessage):
def send(self):
print('send email')
def f1(self):
pass
class Wechat(BaseMessage):
def send(self):
print('send email')
def f2(self):
pass
class Msg(BaseMessage):
def send(self):
print('send message')
def f3(self):
pass
2.源码的约束就是这么写的
#pip3 install djangorestframework
# from rest_framework.authentication import SessionAuthentication
# from rest_framework.authentication import RemoteUserAuthentication
# from rest_framework.authentication import TokenAuthentication
#上面三个类都是继承了下面的类
class BaseAuthentication:
def authenticate(self, request):
raise NotImplementedError(".authenticate() must be overridden") #未实现错误: 这个方法必须被重写
3.示例
class Foo:
def f1(self,name):
raise NotImplementedError('.f1() must be overridden')
class Bar(Foo):
def f1(self,name):
print('in bar')
obj = Bar()
obj.f1('bajie')
4.其他语言中的接口和类, 与python对比
python中没有接口,只有类
类的编写: class 自定义类名:
pass
关于多继承:
允许
关于约束:
java和c#中既有接口,也有类
类的编写: class 自定义类名:
pass
接口的编写: interface 自定义接口名:
pass
接口的用途:
接口中不允许在方法内部写代码,主要用于约束它的子类,必须实现接口中的所有方法
关于多继承:
不允许
但是可以继承多个接口, 这里的继承叫做实现
关于约束: (伪代码)
使用抽象类和抽象方法也可做约束, 约束子类中,必须实现抽象类中的抽象方法(abstract 抽象)
abstract class 自定义类名:
def f1()
abstract def f2()
5.类的分类
分为普通类和抽象类
6.python的另外一种约束:
还支持抽象类和抽象方法进行约束(了解, 基本不用)
from abc import ABCMeta,abstractmethod
class Base(metaclass=ABCMeta): #定义抽象类
def f1(self): #可以定义普通方法
print(123)
@abstractmethod #定义抽象方法
def f2(self):
pass
class Foo(Base):
def f2(self): #如果没有抽象父类的,抽象方法,实例化的时候会报错
print(666)
7.总结:
1.什么是接口?以及作用
接口是一种数据类型, 主要用于约束派生类中必须实现指定的方法
python中不存在, 而java和c#中是存在的
2.python 中是用什么来约束呢
抽象类和抽象方法, 编写比较麻烦
人为主动抛出异常, 编写简单明了
3.抛出异常的异常错误类型, 是否可以用其他异常
不专业: raise Exception(".send() must be overridden")
专业: raise NotImplementError(".send() must be orverridden")
4.以后看代码时, 揣摩心思
5.应用场景
多个类, 内部都必须有某些方法时, 需要使用基类+异常约束
二.异常处理最后一招: 自定义异常
1.方式一: 一个一个条件判断, 返回异常信息
import os
def func(path,prev):
'''
去path路径的文件中, 找到前缀为prev的一行数据, 获取数据并返回给调用者
1000,成功
1001,文件不存在
1002,关键字为空
1003,未知错误
:return:
'''
response = {'code':1000, 'data':None}
try:
if not os.path.exists(path):
response['code'] = 1001
response['data'] = '文件不存在'
return response
if not prev:
response['code'] = 1002
response['data'] = '关键字为空'
return response
a = 1
pass
except Exception as e:
response['code'] = 1003
response['data'] = '未知错误'
return response
return a
def show():
return 8
def run():
v1 = func()
v2 = show()
2.方式二: 自定义异常:
2.1.自定义异常类
2.2.if后主动raise自定义继承Exception的类
2.3.然后再捕获自定义异常,返回给用户具体的异常信息
import os
class ExistsError(Exception): #1.自定义异常类: 继承所有异常类的基类Exception
pass
class KeyInvalidError(Exception):
pass
def func(path,prev):
response = {'code':1000, 'data':None}
try:
if not os.path.exists(path):
raise ExistsError() #2.主动抛出异常: 抛出自定义异常类, 下面就可以捕获你自定义的异常
if not prev:
raise KeyInvalidError()
a = 1
pass
except ExistsError as e: #3.捕获自定义的异常: 在自定义异常里, 定义字典的code和data, 将具体的异常信息返回给用户
response['code'] = 1001
response['data'] = '文件不存在'
return response
except KeyInvalidError as e:
response['code'] = 1002
response['data'] = '关键字为空'
return response
except Exception as e:
response['code'] = 1003
response['data'] = '未知错误'
return response
return a
def show():
return 8
def run():
v1 = func()
v2 = show()
3.异常体现的就是封装
class MyException(Exception):
def __init__(self, code, msg):
self.code = code
self.msg = msg
try:
raise MyException(1000,'操作异常')
except MyException as e: #e 是实例化异常类的对象
print(e.code, e.msg) #可以调用类中的成员
except KeyError as e:
print(e,1111)
except Exception as e:
print(e,2222)
三.hashlib模块: 用于加密的
1.对'字符串'进行加密, 实际是bytes类型的 (hex 十六进制, digest 摘要)
import hashlib
obj = hashlib.md5() #实例化对象
obj.update('bajie'.encode('utf-8')) #写入要加密的字节
rst = obj.hexdigest() #获取密文(结果是没法反解的:说的是算法)(可以反解: 当你的字符串-密文的库足够强大时, 可以撞库出来)
print(rst)
2.如何防止撞库: 加盐
import hashlib
obj = hashlib.md5(b'secretbajie') #加的盐, 也是字节码
obj.update('bajie'.encode('utf-8'))
rst = obj.hexdigest()
print(rst)
3.hashlibd 的应用
如何解密: 解不了,可以密文和密文之间比较
import hashlib
def md5(pwd):
obj = hashlib.md5(b'secretbajie')
obj.update(pwd.encode('utf-8'))
rst = obj.hexdigest()
return rst
usr = input('name: ')
pwd = input('passwd: ')
if usr == 'bajie' and md5(pwd) == '837a5f2beb5ec1330e0f0aeadd8a184a':
print('successful')
4.再谈hashlib
md5, sha256, sha512 名字不同, 加密的复杂度不一样: md5是32位的; 256, 512比32位更复杂, 更安全
但性能和安全要有取舍, 一般md5就够用了
5.当加密的内容比较多, 可多次update()进行追加, 结果和一次update的摘要是一样的
import hashlib
def md5(filename):
md5 = hashlib.md5('bajieaishuishui')
with open(filename,mode='r',encoding='utf-8') as f:
for line in f:
md5.update(line.encode('utf-8'))
else:
return md5.hexdigest()
print(md5('ssh_server.py'))
四.logging模块
1为什么要有日志? 给开发人员看的, 用于排查错误.
logging模块做了什么? 会帮你处理并发, 不会出错, 支持线程安全, 默认日志文件是追加写
import logging
logger = logging.basicConfig(
filename='log.txt',
format='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s', #日志里每条记录的格式,前4个不用传.模块自己会传:
datefmt = '%Y-%m-%d %H:%M:%S',
level=10,) #限制下面的记日志的动作, 只有 >= 这个级别的才记录 #默认是 30
logging.debug('this test line is mydebug') #10 #把 %(message)s 的日志记录写到日志文件里 #测试环境,
logging.info('this test line is myinfo') #20 #把 %(message)s 的日志记录写到日志文件里 #正常信息
logging.warning('this test line is mywarning') #30 #把 %(message)s 的日志记录写到日志文件里 #警告: 只是警告
logging.error('this test line is myerror') #40 #把 %(message)s 的日志记录写到日志文件里 #错了哥
logging.critical('this test line is mycritical') #50 #把 %(message)s 的日志记录写到日志文件里 #严重了哥
logging.log(20, 'this test line is mylog') #可以用上面模块自带的级别, 也可以自己写, 传入对应的数字,(和上面的五个对应的一致)
2.应用1
import logging
def func():
try:
a = a + 1
except Exception as e:
print(e) #print(对象), 实际执行的是 __str__()方法
logging.error(e) # e 是这个类的对象
logging.error(e.__str__()) #专业点, 就是把e.__str__() 写到日志
func()
3.应用, 如何把更详细的信息写到日志里(比如traceback 回溯信息)
import traceback
def func():
try:
name = bajie
except Exception as e:
rst = traceback.format_exc() #拿到trackback信息: 当前错误的堆栈信息
logging.error(rst) #写到日志
func()
4.日志的文件个数, 如何把日志记到不同的文件
4.1.默认只能初始化设置一个日志文件
import logging
logger1 = logging.basicConfig(
filename='log1.txt',
format='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s',
datefmt = '%Y-%m-%d %H:%M:%S',
level=10,)
logging.error('logging1 . ') #正常写日志
logger2 = logging.basicConfig( #logger2 没有生效, 配置不能二次初始化设置
filename='log2.txt',
format='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s',
datefmt = '%Y-%m-%d %H:%M:%S',
level=10,)
logging.error('logging2 . ') #第二次配置没有生效, 这条日志写到了第一个配置时的日志文件中
4.2.把日志写入到多个不同的文件, 需要自己定义自己搞
import logging
f1 = logging.FileHandler('log1.txt','a',encoding='utf-8') #1.搞个文件句柄
f1.setFormatter(logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s")) #2.搞个日志格式
logger1 = logging.Logger('s1',level=logging.ERROR) #3.搞个日志级别
logger1.addHandler(f1) #4.把句柄交给logger1
logger1.error('my name is error, logger1') #5.写日志
f2 = logging.FileHandler('log2.txt','a',encoding='utf-8')
f2.setFormatter(logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s"))
logger2 = logging.Logger('s2',level=logging.ERROR)
logger2.addHandler(f2)
logger2.info('my name is error, logger2')
logger2.error('my name is error, logger2')
logger2.critical('my name is critical, logger2')