约束和异常处理

本节主要内容:

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)
原文地址:https://www.cnblogs.com/af1y/p/9948438.html