• 异常处理和约束


    主要内容:

    • 1. 异常处理
    • 2. 类的约束
    • 3. MD5加密
    • 4. 日志

    1. 异常处理

        异常就是程序在运行过程中产生的错误

     (1)异常处理方案一:

    def calculate(a,b):
        return a/b
    try:
        ret = calculate(10,0)
        print(ret)
    except Exception as e:
        print("除数不能是0")        # 除数不能是0 

      try...except的含义:

    •  尝试着运行xxxxx代码. 出现了错误. 就执行行except后面的代码.
    •  在这个过程中,当代码出现错误的时候,系统会产生⼀个异常对象,然后这个异常会向外抛,被except拦截,并把接收到的异常对象赋值给e
    •  这里的e就是异常对象,这里的Exception是所有异常的基类, 也就是异常的跟,即所有的错误都是Exception的子类对象.

    然而:

          用Exception表示所有的错误,太笼统了,所有的错误都会被认为是Exception. 当程序中出现多种错误的时候, 就不好分类了, 最好是出什么异常就用什么来处理,更加合理了,所以在try...execpt语句中会写更多的except.

    try:    
        print("各种操作....") 
    except ZeroDivisionError as e:    
        print("除数不能是0") 
    except FileNotFoundError as e:    
        print("文件不存在") 
    except Exception as e:    
        print("其他错误")

    此时. 程序运行过程中,如果出现了ZeroDivisionError就会被第⼀个except捕获, 如果出现了FileNotFountError就会被第⼆个except捕获. 如果都不是这两个异常. 那就会被最后的 Exception捕获. 总之最后的Exception就是我们异常处理理的最后一个守门员. 

    完整的异常处理写法(语法): 

    try:
        '''操作'''
    except Exception as e:
    '''异常的父类,可以捕获所有的异常'''
    else:
    '''保护不抛出异常的代码, 当try中无异常的时候执行'''
    finally:
    '''最后总是要执行我'''

    解读:程序先执行操作, 然后如果出错了会走except中的代码,如果不出错, 执行else中的代码,不论出不出错. 最后都要执行finally中的语句,一般try...except就够用了,顶多加上finally,finally⼀般用来作为收尾工作.

    (2)异常处理方案二:

    情境: 执行代码的过程中如果出现了一些条件上的不对等,根本不符合我的代码逻辑. 比如.传递参数时,要求传递个数字,如果用户传递一个字符串 ,此时没办法处理,那如何通知你呢?两个方案的处理方式    

    • 方案一. 直接返回,我不管你还不行么? 
    • 方案二. 抛出一个异常,明确告诉你错误   (代码中如果出现了类似的问题直接抛一个错误出去)
    def add(a,b):
        """传递两个整数,
        我帮你计算两个数的和
        :param :param a:
        :param :param b:
        :return :return:
        """
        if not type(a) == int and not type(b) == int:    #当程序运行到这句话时,整个函数调用就会被中断,并外抛一个异常.
            raise Exception("不是整数,不能进行计算")
        return a + b
    #如果调用方不处理异常,产生的错误就会就绪向外抛,最后就抛了用户
    # add("时间","空间")
    
    # 如果调用方处理的了异常,那么错误就不会丢给用户,程序也能正常运行
    try:
        add("胡辣汤","肉夹馍")
    except Exception as e:
        print("报错了,自己处理吧")

    (3) 自定义的异常

    • 当代码中出现了了⼀个无法用现有的异常来解决问题时,我们需要自定义异常
    • 自定义异常: 当这个类继承了Exception类,那它就成为了一个异常类.
    class GenderError(Exception):
        pass
    class Person:
        def __init__(self,name,gender):
            self.name = name
            self.gender = gender
    
    def nan_zaotang(person):
        if person.gender != "":
            raise GenderError("性别不对,这里是男澡堂")
    p1 = Person("alex","")
    p2 = Person("eggon","")
    
    # nan_zaotang(p1)
    # nan_zaotang(p2)              # 报错,会抛出一个异常  GenderError
    
    #处理异常
    try:
        nan_zaotang(p1)
        nan_zaotang(p2)
    except GenderError as e:
        print(e)              #性别不对,这里是男澡堂
    except Exception as e:
        print("反正错了")

    但是, 如果是真的报错l. 我们在调试的时候, 最好是能看到错误源自于哪里? 这时需要引入另⼀个模块traceback,这个模块可以获取到我们每个方法的调用信息,又被成为堆栈信息.

    import traceback
    class GenderError(Exception):
        pass
    class Person:
        def __init__(self,name,gender):
            self.name = name
            self.gender = gender
    
    def nan_zaotang(person):
        if person.gender != "":
            raise GenderError("性别不对,这里是男澡堂")
    p1 = Person("alex","")
    p2 = Person("eggon","")
    
    # nan_zaotang(p1)
    # nan_zaotang(p2)              # 报错,会抛出一个异常  GenderError
    
    #处理异常
    try:
        nan_zaotang(p1)
        nan_zaotang(p2)
    except GenderError as e:
        val = traceback.format_exc()
        print(e)              #性别不对,这里是男澡堂
        print(val)
    except Exception as e:
        print("反正错了")
    
    #性别不对,这里是男澡堂
    # Traceback (most recent call last):
    #   File "E:/Python_workspace/day020/020整理.py", line 83, in <module>
    #     nan_zaotang(p2)
    #   File "E:/Python_workspace/day020/020整理.py", line 73, in nan_zaotang
    #     raise GenderError("性别不对,这里是男澡堂")
    # GenderError: 性别不对,这里是男澡堂

     当测试代码的时候把堆栈信息打印出来,但是当到了线上的生产环境的时候把这个堆栈去掉即可 

    2. 类的约束

    (1)第一套方案

    • 提取父类. 然后在父类中定义好方法. 在这个方法中什么都不用干,抛⼀个异常就可以了. 这样所有的子类都必须重写这个方法,否则. 访问的时候就会报错.
    • 使用元类来描述父类. 在元类中给出⼀个抽象方法,这样子类就不得不给出抽象方法的具体实现,也可以起到约束的效果.
    class Base:
        def login(self):
            raise Exception("你没有实现login方法()")
    class Normal(Base):
        def login(self):
            pass
    class Member(Base):
        def denglu(self):
            pass
    class Admin(Base):
        def login(self):
            pass
    
    def login(obj):
        print("准备验证码......")
        obj.login()
        print("进入主页........")
    
    n = Normal()
    m = Member()
    a = Admin()
    login(n)
    login(m)                         #报错
    login(a)

       在执行到login(m)的时候程序会报错,原因是, 此时访问的login()是父类中的方法,但是父类中的方法会抛出⼀个异常, 所以报错. 这样程序员就不得不写login方法了,从而对子类进行了相应的约束.

    在本示例中,要注意,我们抛出的是Exception异常,而Exception是所有异常的根. 我们无法通过这个异常来判断出程序是因为什么报的错,所以. 最好是换⼀个比较专业的错误信息,最好 是换成NotImplementError. 其含义是. "没有实现的错误".这样可以一目了然的知道是什么错了

    (2)第二套方案:写抽象类和抽象方法

     在python中编写一个抽象类比较麻烦,需要引入abc模块中的ABCMeta和 abstractmethod这两个内容.

    from abc import ABCMeta, abstractmethod
    class Animal(metaclass=ABCMeta): # 在父类中写出metaclass= xxx  抽象类, 类中存在抽象方法, 类一定是抽象类
    
        @abstractmethod # 抽象方法
        def chi(self): # 抽象的概念.
            pass
        def haha(self):
            print("娃哈哈")
    
    class Cat(Animal): # 子类必须实现父类中的抽象方法.
        def chi(self):  # 具体的实现
            print("猫爱吃鱼")
    # a = Animal()    #抽象类不能创建对象
    c = Cat()
    c.chi()

    这时来解决开始的问题

    from abc import ABCMeta,abstractmethod
    class Base(metaclass=ABCMeta):
        @abstractmethod
        def login(self):pass
    
    # 张三
    class Normal(Base):
        def login(self):
            print("普通人登陆")
    # 李四
    class Member(Base):
        def login(self):
            print("吧务登陆")
    # 王五
    class Admin(Base):
        def login(self):
            print("管理员登陆")
    def login(obj):
        print("产生验证码")
        obj.login() # 标准在这里.  必须由login
        print("进入主页")
    
    # 场景
    n = Normal()
    m = Member()
    a = Admin()
    login(n)
    login(m)
    login(a)

    总结: 

    约束. 其实就是父类对子类进行约束,子类必须要写xxx方法. 在python中约束的方式方法有两种:

    • 1. 使用抽象类和抽象方法, 由于该方案来源是java和c#. 所以使用频率还是很少的
    • 2. 使用人为抛出异常的方案.  并且尽量量抛出的是NotImplementError,这样比较专业,而且错误比较明确(推荐)

    3. MD5加密

    MD5是⼀种不可逆的加密算法,

     它是可靠的,并且安全的,在python中我们不需要手写这⼀套算法,只需要引入⼀个叫hashlib的模块就能搞定MD5的加密工作 

    import hashlib
    # 1. 创建一个MD5对象
    obj = hashlib.md5(b"flkjsdalkfjklasdjfklasjkflasdjklfasdjflkadsj") # 加盐
    
    # 2. 把要加密的内容给md5
    obj.update("alex".encode("utf-8")) # 必须是字节
    
    # 3. 获取密文
    val = obj.hexdigest()   # 534b44a19bf18d20b71ecc4eb77c572f aa7aa5ec13222b27f76a094207c5ac75
    print(val)
    
    def my_md5(val):
        obj = hashlib.md5(b"flkjsdalkfjklasdjfklasjkflasdjklfasdjflkadsj")
        obj.update(val.encode("utf-8"))
        val = obj.hexdigest()
        return val
    
    # 注册的时候. 用md5进行加密. 存储的是加密后的密文
    username = input("请输入用户名")
    password = input("请输入密码")
    # cun = my_md5(password)
    # print(cun)                                 # alex 26adff81aa6778d26999b95ddc0e50b2
    if username == "alex" and my_md5(password) == "26adff81aa6778d26999b95ddc0e50b2":
        print("登录成功")
    else:
        print("登录失败")

    4. 日志

    在python中创建日志系统

    • 导入logging模块.
    • 简单配置⼀一下logging
    • 出现异常的时候(except). 向日志里写错误信息.

    (1)单日志文件的处理

    import logging
    logging.basicConfig(filename='app.log',
                        format='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S',
                        level=0)    # level 设置级别. 当你的信息的级别>=level的时候才会写入日志文件, 默认30
    # CRITICAL = 50
    # FATAL = CRITICAL
    # ERROR = 40
    # WARNING = 30
    # WARN = WARNING
    # INFO = 20
    # DEBUG = 10
    # NOTSET = 0
    # 写日志
    logging.critical("我是critical")
    logging.error("我是error")
    logging.warning("我是警告")
    logging.info("我是基本信息")
    logging.debug("我是调试")
    logging.log(2, "我是自定义")

    检测下:

    import logging
    logging.basicConfig(filename='app.log',
                        format='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S',
                        level=0)    # level 设置级别. 当你的信息的级别>=level的时候才会写入日志文件, 默认30
    # CRITICAL = 50
    # FATAL = CRITICAL
    # ERROR = 40
    # WARNING = 30
    # WARN = WARNING
    # INFO = 20
    # DEBUG = 10
    # NOTSET = 0
    # 写日志
    # logging.critical("我是critical")
    # logging.error("我是error")
    # logging.warning("我是警告")
    # logging.info("我是基本信息")
    # logging.debug("我是调试")
    # logging.log(2, "我是自定义")
    
    import traceback
    for i in range(30):
        try:
            if i%3==0:
                raise FileNotFoundError()
            if i%3==1:
                raise StopIteration()
            if i%3==2:
                raise KeyError()
        except FileNotFoundError as e:
            val = traceback.format_exc()
            logging.error(val)
        except StopIteration as e:
            val = traceback.format_exc()
            logging.error(val)
        except KeyError as e:
            val = traceback.format_exc()
            logging.error(val)
        except Exception as e:
            val = traceback.format_exc()
            logging.error(val)

    (2)多日志文件的处理

    # 创建⼀个操作⽇志的对象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('A', level=40)
    logger1.addHandler(file_handler)
    # 记录日志
    logger1.error('我是A系统')
    
    
    
    # 再创建⼀个操作⽇志的对象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('B', level=40)
    logger2.addHandler(file_handler2)
    # 记录日志
    logger2.error('我是B系统')
  • 相关阅读:
    关于MQ的对比
    关于RabbitMQ(二)
    关于QPS、TPS、并发用户数、吞吐量的关系
    关于使用Ecplise构建gradle项目
    关于记录一次线上真实环境多线程引发的问题
    关于MySQL——find_in_set()函数的使用
    关于数据库的表连接
    关于Java线程池
    IntelliJ搭建Scala及Spark工程
    idea编写wordcount程序及spark-submit运行
  • 原文地址:https://www.cnblogs.com/wcx666/p/9725167.html
Copyright © 2020-2023  润新知