欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

约束和异常处理

程序员文章站 2022-05-20 11:01:57
本节主要内容: 1.类的约束 2.异常处理 3.自定义异常 4.日志 一.类的约束 ⾸先, 你要清楚. 约束是对类的约束. 比如. 现在. 你是一个项⽬经理. 然后呢. 你给手下 的人分活. 张三, 你处理一下普通用户登录, 李四, 你处理一下会员登录, 王五, 你处理一下管理员登录. 那这个时候呢 ......

本节主要内容:

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)