zoukankan      html  css  js  c++  java
  • 约束和异常处理

    本节主要内容:

    1.类的约束

    2.异常处理

    3.自定义异常

    4.日志

    一.类的约束

    ⾸先, 你要清楚. 约束是对类的约束.  比如. 现在. 你是一个项⽬经理. 然后呢. 你给手下 的人分活. 张三, 你处理一下普通用户登录,

    李四, 你处理一下会员登录, 王五, 你处理一下管理员登录. 那这个时候呢. 他们就开始分别取写他们的功能了了. 但是呢. 你要知道,

    程序员不一定会有那么好的默契. 很有可能三个人会写完全三个不同的方法. 就比如这样:

    # 贴吧
    # 项目经理(级别高一点儿)
    class Base:
        def login(self): # 强制子类做xxxx事
            raise NotImplementedError("子类没有实现该方法") # 报错. 抛异常
    
    # 1. 普通账号  -->  张三
    class Normal(Base):
        def login(self):
            print("普通账号的登录")
    
    # 2. 吧务  - > 李四
    class Member(Base):
        def login(self):
            print("吧务的登录")
    
    # 3. 百度员工  -> 王五
    class Admin(Base):
        def login(self): # 方法的覆盖和重写
            print("管理员的登录")
    
    # 项目经理
    def wodetian(obj):
        obj.login()
    
    n = Normal()
    wodetian(n)
    
    m = Member()
    wodetian(m)
    
    a = Admin()
    wodetian(a)

     然后呢, 他们把这样的代码交给你了.  你看一眼. 张三和王五还算OK 这个李四写的是 什么鬼?  denglu.......难受不.

    但是好歹能用. 还能凑合. 但是这时. 你这边要使用了. 问题就来了. 

    对于张三和王五的代码. 没有问题. 但是李四的. 你是不是调用不了. 那如何避免这样的 问题呢?  我们要约束程序的

    结构. 也就是说. 在分配任务之前就应该把功能定义好. 然后分别交给底下的程序员来完成相应的功能. 

    约束的作用:规范代码,约束是对类的约束

    在python中有两种办法解决这样的问题:

    1.提取父类,然后在父类中定义好办法.在这个方法中什么都不用干,就抛出一个异常就可以了,这样所有的子类就必须重写这个方法.

    否则,访问的时候就会报错.

    2.使用元类来描述父类.在元类中给出一个抽象方法.这样子类就不得不给出抽象方法的具体实现.也可以起到约束的效果.

    首先,我们先看第一张解决方案:首先,提取一个父类,在父类中给出一个方法,并且在方法中不给出任何代码,直接抛出异常.

    class Base:
        def login(self):
            raise NotImplementedError
    
    class Normal(Base):
        def login(self):
            print("普通用户登录")
    
    class Member(Base):
        def login(self):
            print("会员登录")
    
    class Admin(Base):
        def denglu(self):
            print("管理员登录")
            
            
    #项目经理写的总入口
    def login(obj):
        print("准备验证码...")
        obj.login()
        print("进入主页...")
    
    n = Normal()
    m = Member()
    a = Admin()
    login(n)
    login(m)
    login(a)       #报错

     在执行到login(a)的时候程序会报错. 原因是, 此时访问的login()是父类中的方法. 但是父类中的方法会抛出一个异常. 所以报错.

    这样程序员就不得不写login方法了. 从而对子类进行了相应的约束.

    在本示例中. 要注意. 我们抛出的是Exception异常. 而Exception是所有异常的根. 我们无法通过这个异常来判断出程序是因为什么

    报的错. 所以. 最好是换一个比较专业的错误信息. 最好是换成NotImplementError. 其含义是. "没有实现的错误". 这样程序员或者项

    ⽬经理理可以一目了然的知道是什么错了. 就好比. 你犯错了. 我就告诉你犯错了. 你也不知道哪里错了. 这时我告诉你, 你xxx错了.

    你改也好改不是? 

    第二套方案: 写抽象类和抽象方法. 这种方案相对来说比上一个麻烦一些. 需要给大家先引入一个抽象的概念我们如果写了一个方法,

    不知道方法的内部应该到底写什么.那这个方法其实就应该是一个抽象的方法.如果一个类中包含抽象方法,那么这个类一定是抽象类

    抽象类是不能有实例对象的.创建对象的时候会报错.

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

    from abc import ABCMeta,abstractmethod
    # 类中包含了抽象方法,那此时这个类就是个抽象类.注意:抽象类可以有普通方法
    class Animal(metaclass=ABCMeta):
        @abstractmethod
        def chi(self):
            pass
    # 抽象类不能创建对象
    class Dog(Animal):
        # 子类必须实现父类中的抽象方法,否则子类也是抽象类
        def chi(self):
            print("躺着吃")
    
    class Cat(Animal):
        def he(self):
            print("喝水")
    
    d = Dog()
    d.chi()
    c = Cat()
    c.he()

    通过代码我们能发现,这里的父类Animal对子类Dog和Cat进行了约束

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

    1.使用抽象类和抽象方法,由于该方案来源是Java和c#.所以使用评率还是很少的

    2.使用人为抛出异常的方案,并且尽量抛出的是NotImplementError.这样比较专业,而且错误比较明确.(推荐)

    二.异常处理

    异常:程序在运行过程中产生的错误.

    def cul(a,b):
        return a/b
    
    ret = cul(10/0)
    print(ret)
    
    结果:
    
    Traceback (most recent call last):
      File "D:/python课件及作业/约束/121.py", line 4, in <module>
        ret = cul(10/0)
    ZeroDivisionError: division by zero

    什么是错误,除法中除数不能是0.那如果真的出了这个错.我们不可能吧一堆错误信息抛给客户,那该如何处理?

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

    try..except的作用就是当程序运行时出现了错误,就执行except后面的代码.在和这个过程中.当代码出现错误的时候,

    系统会产生⼀个异常对象. 然后这个异常会向外抛. 被except拦截. 并把接收到的异常对象赋值给e. 那这里的e就是

    异常对象. 那这里的 Exception是什么? Exception是所有异常的基类, 也就是异常的根. 换句话说. 所有的错误都是

    Exception的子类对象. 我们看到的ZeroDivisionError 其实就是Exception的子类. 那这样 写好像有点儿问题. Exception

    表示所有的错误. 太笼统了了. 所有的错误都会被认为是Exception. 当程序中出现多种错误的时候, 就不好分类了了, 最

    好是出什么异常就⽤用什么来处理. 这样就更加合理了. 所以在try...execpt语句中. 还可以写更多的except.

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

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

    解读:程序先执行操作,然后如果出错了会走except中的代码.如果不出错,执行else中的代码.不论出不出错,最后都要

    执行finally中的语句,一般我们用try...except就够用了.顶多加上finally.finally一般用来作为收尾工作.

    以上是处理异常,我们在执行代码的过程中如果出现了一些条件上的不对等.根本不符合我的代码逻辑.比如,参数.我要求

    传递的是一个数字,而客户非得传递一个字符串.那我们该如何处理来通知客户呢?

    方案一:直接返回即可.

    方案二:抛出一个异常.

    那如何抛出异常呢?我们要用到关键字raise

    def add(a,b):
        """
        传递两个整数,求和
        :param a: 
        :param b: 
        :return: 
        """
        if not type(a) == int or not type(b) == int:
            #当程序运行到这句话的时候,正割函数的调用就会被中断,并向外抛出一个异常
            raise Exception("不是整数,无法求和")
        return  a + b
    
    # 如果调用方不处理异常,那产生的错误将会继续向外抛,最后就抛给了用户
    #  如果调用方处理了异常. 那么错误就不会丢给用户. 程序也能正常运行
    try:
        add("胡辣汤",1)
    except Exception as e:
    

    当程序运行到raise. 程序会被中断. 并实例化后面的异常对象. 抛给调用方.  如果调用方不处理. 则会把错误继续向上抛出. 最终抛给⽤用户.

    如果调用方处理了异常. 那程序可以正常的进行执行.

    三.自定义异常

    自定义异常:非常简单,只要你的类继承了Exception类,那你的类就是一个异常类.

    import traceback
    
    class GenderError(Exception):
        pass
    
    class Person:
        def __init__(self,name,gender):
            self.name = name
            self.gender = gender
    
        def goto_WC(self):
            if self.gender == "":
                print("进来吧")
            else:
                raise  GenderError("错了,不可以进来")
    
    try:
        p1 = Person("Andy","")
        p1.goto_WC()
        p2 = Person("Amy","")
        p2.goto_WC()
    except GenderError as e:
        val = traceback.format_exc()
        print("你不是男的,别来啊")
        print(val)
    except Exception as e:
        print("其他错误)

    结果:

    进来吧
    你不是男的,别来啊
    Traceback (most recent call last):
    File "D:/python课件及作业/约束/约束.py", line 83, in <module>
    p2.goto_WC()
    File "D:/python课件及作业/约束/约束.py", line 77, in goto_WC
    raise GenderError("错了,不可以进来")
    GenderError: 错了,不可以进来

    我们在调试的时候最好是能看到错误院子哪里,那怎么办?

    上面的代码引入了另一个模块traceback,这个模块可以获取到我们每个方法的调用信息.又被称为堆栈信息,

    这个信息对我们拍错是很有帮助的

    四.日志

    在编写任何一款软件的时候,都会出现各种各样的问题或者bug,这些问题或者bug一般都会在测试的时候处理掉.

    但是多多少少的都会出现一些意想不到的异常或者错误.那这个时候,我们是不知道哪里出现了问题.因为很多都

    不是必现的bug.如果是必现的,测试的时候肯定能测出来.最头疼的就是这种不必现的bug.自己运行没有问题,但

    是到客户那一用就出问题.那怎么办?我们需要给软件准备一套日志系统.当出现任何错误的时候.我们都可以去日

    志系统里去查看.看哪里出了问题.这样解决问题和bug的时候就多了一个帮手.那如何在python中创建这个日志系

    统呢?很简单:

    1.导入logging模块.

    2.简单配置一下logging

    3.出现异常的时候(except).向日志里写错误信息.

    import logging
    #filename:文件名
    # format:数据的格式化输出.最终在日志文件中的样子
    #       时间-名称-级别-模块: 错误信息
    # datefmt:时间的格式
    # level:错误的级别权重,当错误的级别权重大于等于leval的时候才会写入文件
    logging.basicConfig(filename="x1.log",format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:'
                                                 '%(message)s',datefmt='%Y-%m-%d %H:%M:%S', level=10)
    #当前配置表示10以上的分数会被写入日志文件
    # critical = 50
    # fatal = critical
    # error = 40
    # warning = 30
    # warn = warning
    # info = 20
    # debug = 10
    # notest = 0
    logging.critical("我是critical")  # 50分.最贵的
    logging.error("我是error")    # 40分
    logging.warning("我是warning")    # 警告 30分
    logging.info("我是基本信息")  # 20
    logging.debug("我是测试")   # 10
    logging.log(2,"我是自定义")  # 自定义,看着给分

    在上面这个模板的基础上做个简单的测试,应用下

    import traceback
    class JackError(Exception):
        pass
    
    for i in range(10):
        try:
            if i % 3 == 0:
                raise FileNotFoundError("文件不在啊")
            if i % 3 == 1:
                raise KeyError("键错了")
            if i % 3 == 2:
                raise JackError
        except FileNotFoundError:
            val = traceback.format_exc()
            logging.error(val)
        except KeyError:
            val = traceback.format_exc()
            logging.error(val)
        except JackError:
            val = traceback.format_exc()
            logging.error(val)
        except Exception:
            val = traceback.format_exc()
            logging.error(val)

     最后,如果你系统中想要把日志文件分开.比如,一个大项目,有两个子系统,那两个子系统要分开记录日志,方便调试.

    那怎么办呢?注意:用上面的basicConfig是搞不定的,我们要借助文件助手(FileHandler),来帮我们完成日志的分开记录:

    import 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("日志1",level=logging.ERROR)
    logger1.addHandler(file_Handler)
    
    logger1.error("我是A系统")
    
    # 再创建一个操作日志的对象logger(依赖FileHandler)
    file_Handler = logging.FileHandler("l2.log", "a", encoding="utf-8")
    file_Handler.setFormatter(logging.Formatter(fmt="%(asctime)s - %(name)s - % (levelname)s -%(module)s:%(message)s"))
    
    logger2 = logging.Logger("日志2",level=logging.ERROR)
    logger2.addHandler(file_Handler)
  • 相关阅读:
    慕课网-安卓工程师初养成-6-3 如何使用 Java 中的数组
    慕课网-安卓工程师初养成-6-2 练习题
    慕课网-安卓工程师初养成-6-1 什么是数组
    慕课网-安卓工程师初养成-5-5 进阶练习
    慕课网-安卓工程师初养成-5-4 使用 Eclipse 调试程序
    慕课网-安卓工程师初养成-5-3 编程练习优化
    慕课网-安卓工程师初养成-5-2 编程练习解析
    币种统计
    先取不败游戏
    贪心算法——数列操作得到最大的值
  • 原文地址:https://www.cnblogs.com/af1y/p/9948438.html
Copyright © 2011-2022 走看看