当前位置: 首页 > news >正文

异常与错误处理高级用法

异常与错误处理高级用法

1.概述

这篇文章介绍如何优雅的使用异常处理好程序的错误、用更少的代码、清晰的代码,写出更健壮的程序。

2.异常高级用法

2.1.两种编程风格处理错误

使用Python处理错误有两种风格,下面介绍下这两种风格。

  • LBYL(look before you leap):LBYL常备翻译成“三思而后行”,通俗讲就是在执行一个可能会出错的操作时,先做一些关键条件判断,仅当条件满足时才进行操作。
    LBYL是一种本能的思考结果,他的逻辑就像“如果天气预报说今天会下雨,那么我就不出门”
  • EAFP(easier to ask for forgiveness than permission),可直译为“获取原谅比许可简单”。 是一种和三思而后行截然不同的编程风格,它不做任何事前检查,直接执行操作,但在外层用try来捕获异常。
    这种做法类似于“出门前不看天气预报,如果下雨了就回家吃感冒药”

在python社区更偏向于使用EAFP编程风格,它的代码通常更精简。因为它不需要开发者用分支覆盖各种可能出错的情况,只需要捕获可能发生的异常情况即可。
EAFP性能更好,它直奔主要代码,省去了各种条件判断。

1.三思而后行编程风格

def incr_by_one(value):
    '''
    输入的参数加1返回新值
    :param value: 
    :return: 
    '''
    if isinstance(value, int):
        return value + 1
    elif isinstance(value, str) and value.isdigit():
        return int(value) + 1
    else:
        print(f'Unable to perform incr for value: {value}')

1.仅当类型是int类型时才执行加法操作
2.判断仅当类型时str,同时满足.isdigit() 方法时才进行操作。
这几行代码看似简单,其实代表了LBYL编程风格。

2.获取原谅比许可简单编程风格

def incr_by_try(value):
    '''
    输入的参数加1返回新值
    :param value:
    :return:
    '''
    try:
        return int(value) + 1
    except (KeyError, ValueError):
        print(f'Unable to perform incr for value: {value}')

2.2.把更精确的except语句放在前面

python内置异常类之间存在许多继承关系,举个简单的依赖关系。
BaseException —> Exception—> LookupError—> KeyError

如果一个try代码块里包含多条except,异常匹配会按照从上而下的顺序执行。假如把一个父类异常放在前面,就会导致子类异常永远不会执行。

这个示例中KeyError异常永远不会被执行,要修复这个问题就要调整except的顺序。

def incr_by_key(d, key):
    try:
        d[key] + 1
    except Exception as e:
        print(f'Unknown error: {e}')
    except KeyError:
        print(f'key {key} does not exists')

2.3.try 搭配 else分支增强代码能力

用try捕获异常时,有时程序需要在一切操作没有异常后执行某个操作。为了做到这一点我们需要创建一个变量来做标记实现这个功能,如下示例。

只有当sync_profile()执行成功时,才继续调用send_notification()发送消息通知。为此我们定义一个额外变量syn_succeeded来作为标记。

def send_mess():
    syn_succeeded = False
    try:
        sync_profile(user.profile)
        syn_succeeded = True
    except Exception as e:
        print(f'Unknown error: {e}')

    if syn_succeeded:
        send_notification(user, 'profile sync succeeded')

如果使用try搭配else分支,代码可以变得更简单。
异常捕获语句里的else表示:仅当try语句块里没有抛出任何异常时,才执行else分支下的内容,效果就像在try最后增加一个标记变量一样。

def sen_mess_try_else():
    try:
        sync_profile(user.profile)
        syn_succeeded = True
    except Exception as e:
        print(f'Unknown error: {e}')
    else:
        send_notification(user, 'profile sync succeeded')

2.4.使用空raise语句

在处理异常时,有时我们可能仅仅想记录下某个异常,然后把它重新抛出,交由上层处理。这是使用不带任何参数的raise语句可以派上用场。

当一个空raise出现在except时,他会原封不动的重新抛出当前异常,因此print语句不会执行。

def incr_by_raise(value):
    try:

        return value + 1
    except TypeError:
        print(f'key {value} does not exists')
        raise

incr_by_raise('dd')

2.5.抛出异常而不是返回错误

python函数支持一次返回多个值,当我们表名函数执行出错时,可以让它同时返回结果与错误信息。
下面是create_item()函数就利用了这个特性,在这段代码里,create_item()函数的功能是创建新的Item对象。当调用create_item()函数,如果执行失败函数会把错误信息放到第二个结果中返回。而当函数执行成功时,为了保持返回值统一,函数同样返回错误原因,只是内容为空字符串。

这种做法看上去很自然,但在python世界里,返回错误并非解决此类问题的最佳办法。这是因为这种做法会增加调用方处理错误的成本

MAX_LENGTH_OF_NAME = 10
MAX_ITEMS_QUOTA = 5
def cerate_item(name):
    '''
    接收名称,创建item对象
    :param name: 
    :return: 返回 结果,错误信息。如果执行成功返回错误信息为空
    '''
    if len(name) > MAX_LENGTH_OF_NAME:
        return None, 'name of item is too long'
    if len(get_current_items()) > MAX_ITEMS_QUOTA:
        return None, 'item is full'
    # 当执行成功后返回结果和空错误信息
    return Item(name), ''

python有完善的异常处理机制,在某种程度上鼓励我们使用异常,所以用异常来处理错误才是更地道的做法

通过引入自定义异常类,上面的代码可以改成下面的样子

MAX_LENGTH_OF_NAME = 10
MAX_ITEMS_QUOTA = 5
Item = []

def get_current_items():
    return len(Item)

# 创建自定义异常类
class CreateItemError(Exception):
    pass


def create_item_except(name):
    if len(name) > MAX_LENGTH_OF_NAME:
    	# 向上抛出异常信息
        raise CreateItemError('name of item is too long')
    if len(get_current_items()) > MAX_ITEMS_QUOTA:
        raise CreateItemError('item is full')
    return Item(name), ''


def create_from_input():
    name = input()
    try:
        item = create_item_except(name)
    except CreateItemError as e:
        print(f'create item failed: {e}')
    else:
        print(f'item<{name}> created')

create_from_input()

#运行结果
create item failed: name of item is too long

用抛出异常代替返回错误后,整个代码结构看上去变化不大但细节上改变非常多。

  • 新函数拥有更稳定的返回值类型,他永远只会返回Item类型或是抛出异常。
  • 不同于返回值,异常在被捕获前会不断往调用栈上层汇报。因此create_item()的直接调用方可以完全不用处理CreateItemError,而交由更上层处理。异常的这个特点给了我们更多灵活性,但同时也带来了风险。假如程序缺少一个顶级的统一异常处理逻辑,那么某个被所有人忽略的异常可能会层层上报,最终弄垮整个程序。
  • 虽然我们鼓励使用异常,但异常总是不可避免的然人感到“惊讶”所以最好在函数文档里说明可能抛出的异常类型。

相关文章:

  • Java学习----Map接口
  • 设计模式-概述. 类图.软件设计原则详细讲解
  • 条件渲染(v-if、v-show)、列表渲染(v-for)、列表中key的原理和作用、列表过滤(filter)、列表排序(sort)
  • openarena
  • 【蓝桥杯省赛真题37】Scratch三国演义字数统计 少儿编程scratch编程蓝桥杯省赛真题讲解
  • Linux内核设计与实现 第三章 进程管理
  • Exception in thread “main“ java.lang.NoClassDefFoundError: org/apache/flink/
  • springboot项目整理(持续更新)
  • Linux Shell重定向 管道命令 awk编程 sed文件操作高阶函数
  • jQuery表单选择器:快速选择input标签
  • 6、Java——三种方式循环出水仙花数
  • 前端核心二:VUE
  • Javassist基本用法
  • 电子学会2022年6月青少年软件编程(图形化)等级考试试卷(一级)
  • 【Vue 基础知识】keep-alive是什么?怎么用?
  • ----------
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • Android 控件背景颜色处理
  • CNN 在图像分割中的简史:从 R-CNN 到 Mask R-CNN
  • Mac转Windows的拯救指南
  • SpiderData 2019年2月25日 DApp数据排行榜
  • Spring Security中异常上抛机制及对于转型处理的一些感悟
  • Twitter赢在开放,三年创造奇迹
  • 从伪并行的 Python 多线程说起
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 工作中总结前端开发流程--vue项目
  • 聊聊directory traversal attack
  • 设计模式走一遍---观察者模式
  • 微信小程序开发问题汇总
  • ​linux启动进程的方式
  • #HarmonyOS:Web组件的使用
  • #Ubuntu(修改root信息)
  • (SpringBoot)第二章:Spring创建和使用
  • (附源码)spring boot基于Java的电影院售票与管理系统毕业设计 011449
  • (附源码)springboot 校园学生兼职系统 毕业设计 742122
  • (转)c++ std::pair 与 std::make
  • ***详解账号泄露:全球约1亿用户已泄露
  • .net core 6 集成 elasticsearch 并 使用分词器
  • .NET Standard 的管理策略
  • .netcore 如何获取系统中所有session_ASP.NET Core如何解决分布式Session一致性问题
  • .Net多线程总结
  • .Net各种迷惑命名解释
  • .vue文件怎么使用_vue调试工具vue-devtools的安装
  • @JoinTable会自动删除关联表的数据
  • @Tag和@Operation标签失效问题。SpringDoc 2.2.0(OpenApi 3)和Spring Boot 3.1.1集成
  • [ C++ ] template 模板进阶 (特化,分离编译)
  • [ vulhub漏洞复现篇 ] JBOSS AS 4.x以下反序列化远程代码执行漏洞CVE-2017-7504
  • [ vulhub漏洞复现篇 ] struts2远程代码执行漏洞 S2-005 (CVE-2010-1870)
  • [20160807][系统设计的三次迭代]
  • [C语言][C++][时间复杂度详解分析]二分查找——杨氏矩阵查找数字详解!!!
  • [dart学习]第四篇:函数
  • [dfs] 图案计数
  • [GXYCTF2019]禁止套娃
  • [HeadFrist-HTMLCSS学习笔记][第一章Web语言:开始了解HTML]
  • [J2ME]如何替换Google Map静态地图自带的Marker