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
'''

 

posted @ 2018-08-31 15:42  葡萄想柠檬  Views(195)  Comments(0)    收藏  举报
目录代码