python 面向对象 约束 + 自定义异常 +加密(hashlib)+ 日志(logging)
约束
- 约束是写在基类中, 用来约束其派生类 : 保证派生类中必须编写哪些方法, 不然执行可能就会报错.
约束的形式有两种:
- 人为主动抛出异常 : (推荐使用))
raise NotImplementedError (".......")
- 使用抽象类和抽象方法 :
from abc import ABCMeta, abstractmethod
其中: ABCMeta 是抽象类, abstractmethod 抽象方法
1. 人为主动抛出异常
示例 : 发送警报通知, 可以通过 微信/短信/邮件 形式发送,
需求 : 分别用三个类完成, 其中每个类中必须要有send()方法.
class BaseMessage(object):
def send(self,x1):
"""
必须继承BaseMessage,然后其中派生类中必须编写send方法。用于完成具体业务逻辑。
"""
raise NotImplementedError(".send() 必须被重写.") # 通过主动抛出异常来约束派生类中要写的方法
class Email(BaseMessage):
def send(self,x1):
"""
必须继承BaseMessage,然后其中必须编写send方法。用于完成具体业务逻辑。
"""
print('发送邮件')
class Msg(BaseMessage):
def send(self,x1):
print('发送短信')
class Wechat(BaseMessage):
def send(self,x1):
print('发送微信')
def func(arg):
"""
报警通知的功能
"""
arg.send() # 执行send方法
obj = Msg()
func(obj) # 将对象传入func函数中
2. 使用抽象类和抽象方法
需要导入模块, 一般此方法不建议使用, 大多数源码中使用的是 主动抛出异常.
抽象类中, 既可以有实现功能的代码, 也可以有抽象方法用来约束子类必须要写的方法
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)
obj = Foo()
obj.f1()
3. 其他语言( java / c# )中的约束
类 :
# java c# 中普通类和python 一样
class Foo:
def f1(self):
pass
def f2(self):
pass
class Bar(Foo):
def f1(self):
pass
'''
抽象类, 约束继承它的派生类中必须实现它其中的方法
以下声明类是不对的,是用python的语法写的,只是表达约束的意思
'''
abstact class Foo: # 抽象类 用abstract
def f1(self):
print(1 ,3 ,4)
abstact def f2(self) :pass # 抽象方法
class Bar(Foo): # 继承抽象类 ,要实现父类的抽象方法f2
def f2(self):
print('111')
接口 :
'''
接口,接口中不允许在方法内部写代码,
只能约束继承它的类必须实现接口中定义的所有方法。
'''
interface IFoo: # 接口用Interface
def f1(self, x1): pass
def f2(self, x1): pass
interface IBar:
def f3(self, x1): pass
def f4(self, x1): pass
class Foo(IFoo, IBar): # 实现了2个接口 类可以实现多个接口
# 类中必须要实现继承的接口中的所有方法
def f1(self, x1): pass
def f2(self, x1): pass
def f3(self, x1): pass
def f4(self, x1): pass
总结 : (可用于面试)
1. 什么是接口以及作用?
接口是一种数据类型, 主要用于约束派生类中必须要实现指定的方法. python 中不存在接口, java / c# 中存在.
2. python 中使用什么来约束呢?
人为主动抛出异常 (推荐使用)
抽象类 + 抽象方法 (编写上比较麻烦)
3. 约束时, 抛出的异常是否可以用其他的?
可以用其他的,但是会显得非常不专业.
不专业 : raise Exception ("......")
专业 : raise NotImplementedEroor ("......")
4. 以后看代码,要揣摩开发者的心思
5. 写代码
class BaseMessage(object):
def send(self,x1):
"""
必须继承BaseMessage,然后其中必须编写send方法。用于完成具体业务逻辑。
"""
raise NotImplementedError(".send() 必须被重写.")
class Email(BaseMessage):
def send(self,x1):
"""
必须继承BaseMessage,然后其中必须编写send方法。用于完成具体业务逻辑。
"""
print('发送邮件')
obj = Email()
obj.send(1)
6. 约束的应用场景
多个类, 内部都必须有某些方法是,需要使用 基类+异常 进行约束.
例如 学院管理程序, 老师 / 学生 / 管理员 都要有登录, 即在各自类中都要有 登录 / 注销 的功能,可以再基类中进行约束.
class IBase:
def login(self):
raise NotImplementedError(".send() 必须被重写.")
class Student:
def login(self):
pass
def score(self):
pass
class Teacher:
def login(self):
pass
def exam(self):
pass
class Manager:
def login(self):
pass
....
自定义异常
自定义异常是为了使代码处理异常的时候更加的简洁明了, 有时候不 自定义异常 代码也会正常运行,但是处理异常会看起来杂乱无章.
自定义异常 就是定义一个 错误类型(MyException) 必须继承 Exception类. 在类中封装错误的信息, 处理异常的时候用 except 来捕获定义的错误类型(MyException).
# 知识点:如何自定义异常类?
class MyException(Exception):
def __init__(self,code,msg):
self.code = code
self.msg = msg
try:
# 知识点:主动抛出异常
raise MyException(1000,'操作异常') # 初始化错误信息
except KeyError as obj:
print(obj,1111)
except MyException as obj: # 知识点:捕获异常 obj是MyException创建的对象
print(obj,2222)
print(obj.code, obj.msg) # 两者都可以
except Exception as obj:
print(obj,3333)
# (1000, '操作异常') 2222 # 以元组的形式打印,222是用来与其他的异常区分不用管
# 1000 操作异常
示例 :
path路径的文件中,找到前缀为prev的一行数据,获取数据并返回给调用者
需要判断 文件是否存在,关键字是够为空,其他未知的一些错误.
'''
某些异常处理时,不用自定义异常,会显得特别复杂.
自定义异常的推导: 以下异常的处理方式虽复杂,但不会影响程序的运行
'''
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
pass
except Exception as e:
response['code'] = 1003
response['data'] = '未知错误'
return response
# 示例二
class ExistsError(Exception):
pass
class KeyInvalidError(Exception):
pass
def new_func(path,prev):
"""
去path路径的文件中,找到前缀为prev的一行数据,获取数据并返回给调用者。
1000,成功
1001,文件不存在
1002,关键字为空
1003,未知错误
...
:return:
"""
response = {'code':1000,'data':None}
try:
if not os.path.exists(path):
raise ExistsError()
if not prev:
raise KeyInvalidError()
pass
except ExistsError as e:
response['code'] = 1001
response['data'] = '文件不存在'
except KeyInvalidError as e:
response['code'] = 1002
response['data'] = '关键字为空'
except Exception as e:
response['code'] = 1003
response['data'] = '未知错误'
return response
加密 (hashlib)
hashlib 加密 需要导入模块 import hashlib
加密采用的是 md5 加密, 该方法的加密不会被反解.
加密主要是对字节进行加密 (转为密文), 所以加密的时候需要将字符串解码为字节 (utf-8).
1. 撞库
md5在线解密: http://www.cmd5.com/
虽然不能反解, 但是不加盐的加密,在该工具中有时候可以查到明文, 该工具的机制是,尝试与数据库进行对比.
obj = hashlib.md5() # 没有参数的情况下就是没有 加盐
2. 加盐
加盐 是为了让明文更加保密,使得 其他工具也难以对比出.
obj = hashlib.md5( b'hueufh787uey7fe8fhhc8' ) # 加盐就是参数中 b"....." 里面可以是任意的.
import hashlib # 导入模块
salt = b'2erer3asdfwerxdf34sdfsdfs90' # 加盐可设为固定的,以后不要随意改变,
# 变了以后自己以后也找不到,因为没有反解
def md5_jiami(pwd):
# 实例化对象
obj = hashlib.md5(salt) # 加盐
# 写入要加密的字节
obj.update(pwd.encode('utf-8'))
# 获取密文
return obj.hexdigest() # c5395258d82599e5f1bec3be1e4dea4a 对应的明文 admin
user = input("请输入用户名:")
pwd = input("请输入密码:")
if user == 'oldboy' and md5_jiami(pwd) == 'c5395258d82599e5f1bec3be1e4dea4a':
print('登录成功')
else:
print('登录失败')
日志 (logging)
1. 为什么要有日志?
日志是为了给开发人员看,用于排查错误.
2. 创建日志文件
需要导入模块: import logging
loggser = logging.basicConfig(.......设置输出参数.........)
logging.error('.....输出信息......')
import logging # 创建日志文件,格式化信息 logger = logging.basicConfig(filename='xxxxxxx.txt', format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S', level=30) # 示例一 并未追踪到具体的位置 logging.debug('x1') # 级别10 logging.info('x2') # 20 logging.warning('x3') # 30 logging.error('x4') # 40 logging.critical('x5') # 50 logging.log(10,'x6') ''' # 因为 level>=30 才会记录到日志中. 结果如下: 2018-08-31 19:22:21 - root - WARNING -10.日志: x3 2018-08-31 19:22:21 - root - ERROR -10.日志: x4 2018-08-31 19:22:21 - root - CRITICAL -10.日志: x5 '''
3. 追踪错误信息, 获取错误信息的具体位置
需要导入模块: import traceback
msg = traceback.format_exc()
import logging
# 创建日志文件
logger = tra`logging.basicConfig(filename='xxxxxxx.txt',
format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
level=30)
# 示例二 获取了具体的错误位置
import traceback
def func():
try:
a = a +1
except Exception as e:
# 获取当前错误的堆栈信息
msg = traceback.format_exc() # 获取具体的错误位置信息
logging.error(msg)
func()
'''
# 写入 xxxxxxx.txt文件中, msg获取的是哪一行信息,具体的位置. 结果如下:
2018-08-31 18:50:33 - root - ERROR -10.日志: Traceback (most recent call last):
File "E:/E 资料/PythonStudy/workplace/code/10.日志.py", line 21, in func
a = a +1
UnboundLocalError: local variable 'a' referenced before assignment
'''
4. 自定义多文件日志
创建日志文件的时候, 连续的创建两个日志文件, 程序会默认将错误都写在第一个之内, 第二个日志文件被创建的时候,会判断是否已经存在,如果存在就不再创建.
import logging logger1 = logging.basicConfig(filename='x1.txt', format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S', level=30) logging.error('x4') # logger2 不会再被创建 logger2 = logging.basicConfig(filename='x2.txt', format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S', level=30) logging.error('x5') '''结果 : 两条错误都会被写入到 x1.txt 文件中. 2018-08-31 19:56:13 - root - ERROR -12.日志文件个数: x4 2018-08-31 19:56:13 - root - ERROR -12.日志文件个数: x5 '''
♥ 那么如何创建两个日志文件呢, 按照我们自己的想法,不同的错误,存储到不同的文件中?
依赖于 FileHandler 创建操作日志的对象.
import logging # 导入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('s1', level=logging.ERROR) logger1.addHandler(file_handler) logger1.error('123123123') # 在创建一个操作日志的对象logger(依赖FileHandler) file_handler2 = logging.FileHandler('l2.log', 'a', encoding='utf-8') file_handler2.setFormatter(logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s")) logger2 = logging.Logger('s2', level=logging.ERROR) # logging.ERROR 的级别为30 logger2.addHandler(file_handler2) logger2.error('666') '''结果: l1.log文件中: 2018-08-31 19:59:24,624 - s1 - ERROR -13.自定义日志: 123123123 l2.log文件中: 2018-08-31 19:59:24,624 - s2 - ERROR -13.自定义日志: 666 '''


浙公网安备 33010602011771号