Python编程中一些异常处理的小技巧

本文介绍了Python编程中自定义异常的方法及API异常处理的最佳实践。包括如何定义异常类、API异常的定义与捕获技巧,以及如何使用异常替代返回状态码等编程艺术。

编程中经常会需要使用到异常处理的情况,在阅读了一些资料后,整理了关于异常处理的一些小技巧记录如下。

1 如何自定义异常

1.1 定义异常类

在实际编程中,有时会发现Python提供的内建异常的不够用,我们需要在特殊业务场景下的异常。这时就需要我们来定义自己的异常。按照Python约定俗成的习惯,用户定义的异常一般都是继承于Exception类,由它开始拓展。后面我们可以看到这样做在捕获异常的时候会带来很大的便利。

>>> class MyError(Exception):
        pass

>>> raise MyError(u"something error")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
__main__.MyError: something error  

1.2 API异常相关的技巧

API的异常分为定义异常与调用API时如何捕获异常两个部分,这二者相辅相成。

1.2.1 定义API异常的技巧

在自己编写API的时候,应该定义Root Exception——API中的根异常,其它异常都继承于它。这样的做法有两个好处:

  1. API代码层次更清晰

  2. API与调用程序代码隔离

假设存在如下场景:需要做一个链接数据库服务的模块。提供一个connect函数用于链接。那么,在链接的过程中,就会发生以下几种情况:

  • socket连接超时

  • socket拒绝连接

针对以上的情况,我们在模块中定义几个异常:

# database.py
class Error(Exception):
    """Root exception for all exceptions raised by this module."""
    
class SocketTimeError(Error):
    pass

class SocketRefuseError(Error):
    pass
    
def connect():
    pass    
1.2.2 调用API时异常捕获的技巧

这样在调用API的时候就可以这样使用:

try:
    connect()
except SocketTimeError as err:
    log.error(err)
except SocketRefuseError as err:
    log.error(err)
except Error as err:
    log.error("API Unexpected error:%s" % err)
except Exception:
    log.error("API bug cause exception.")    

这样精确定义多个异常,使得代码层次清晰,增强了可读性。值得注意的是:在代码的最后还捕获了Error以及Exception两个异常,这两个操作分别对应于可拓展性与健壮性的目的。

捕获Root Exception以提高可拓展性:

我们知道,在实际链接数据库时,还可能会出现用户没有登陆权限等问题。所以,我们需要在下一个版本中加入PermissionDeny这个异常。但是,旧的调用代码已经写好了,如果忘记修改的话,这个异常可能就会无法被处理,进而使得调用的程序奔溃。处于这样的考虑,我们在调用API的时候,就应该再捕获API的Root Exception,即使之后新加入了其它的异常,在这一个except中也能被捕获而不影响调用程序。使得API模块的可拓展性得到了提高。

捕获Exception以提高健壮性:

在调用API的时候,难免可能出现API内部存在bug的情况。这个时候如果捕获了Exception的话,就算API内部因为bug发生了异常,也不会影响到调用程序的正常运行。

从这两点中可以看出,要达到这种效果,其实都要依赖于常规异常继承于Exception类这个规矩。这样的架构划分所带来的好处是显而易见的。

2 与异常相关的编程艺术

2.1 异常代替返回状态码

我们经常需要编写一些工具类的函数,往往在这些函数的处理流程中,会产生很多的状态;而这些状态也是调用者需要得到的信息。很多时候,会用一些具有意义的返回值来表示函数处理的状态。

比如:

def write(content):
    if isinstance(content, basestring):
        f_handler = open("file.txt", 'w')
        try:
            f_handler.write(context)
            except Exception:
                return -2    # write file fail
        else:
            return 0    # write file succcess
        finally:
            f_hanlder.close()
    else:
        return -1    # arg type error

调用代码:

result = write()
if result == -1:
    log.error(u"type error")
elif result = -2:
    log.error(u"write error")
else:
    log.info("ok")    

这种状态码的方式使用起来特别的不方便,调用者还需要去理解每个状态码的意义,带来其它的学习成本;而且用if-else结构也不易于后期的程序拓展。所以,我们可以使用触发异常来代替返回状态码,每个异常名其实就包含了状态的意义在内(命名的艺术),使用起来也更好理解。

使用异常的方式:

class Error(Exception):
    pass
    
class OpenFileError(Error):
    pass
    
class WriteContentError(Error):
    pass    

def write(content):
    if isinstance(content, basestring):
        f_handler = open("file.txt", 'w')
        try:
            f_handler.write(context)
            except Exception:
                raise WriteContentError
        finally:
            f_hanlder.close()
    else:
        raise OpenFileError

调用代码:

try:
    write()
except OpenFileError as e:
    log.error(e)
except WriteContentError as e:
    log.error(e)
except Error:
    log.error("API Error")
except Exception
    log.error("API Bug")    
else:
    log.info("ok")

结合上面一点提到的使用API时的异常捕获,使得调用代码变得更佳灵活。

2.3 异常处理与流程控制

错误处理很重要,但如果它搞乱了代码逻辑,就是错误的做法

将异常处理与正常流程控制混为一谈时,代码是十分丑陋的。我们应该将二者分离,最好的做法就是将异常代码块抽离到另外的函数中。

try:
    action_a()
    action_b()
    action_c()
except ActionException as e:
    log.error(e)
else:
    action_d()    

将异常处理分离:

def action_executor():
    action_a()
    action_b()
    action_c()
    
def action():
    try:
        action_executor()
    except ActionException as e:
        log.error(e)
        
action()
action_d()

3 参考资料

第1章 绪论 1.1 地理信息系统的概念 1.2 地理信息系统的组成 1.2.1 系统硬件 1.2.2 系统软件 1.2.3 空间数据 1.2.4 应用人员 1.2.5 应用模型 1.3 地理信息系统的功能 1.3.1 基本功能 1.3.1 应用功能 1.4 地理信息系统的发展 1.4.1 GIS发展简史 1.4.2 当代GIS发展动态 参考题 第2章 地理信息系统的空间数据结构和数据库 2.1 空间数据结构 2.1.1 概述 2.1.2 矢量数据结构 2.1.3 栅格数据结构 2.1.4 矢量栅格本体化数据结构 2.1.5 三维数据结构 2.2 GIS的数据模型 2.2.1 概述 2.2.2 层次数据模型 2.2.3 网状数据模型 2.2.4 关系数据模型 2.2.5 对象数据模型 2.2.6 时空数据模型 2.3 空间数据库的设计 2.3.1 数据的管理模式 2.3.2 空间数据库的设计、建立与维护 参考题 第3章 空间数据的采集和质量控制 3.1 概述 3.1.1 GIS的数据源 3.1.2 空间数据采集的任务 3.1.3 研究GIS数据质量的目的和意义 3.2 空间数据的地理参照系和控制基础 3.2.1 空间数据的地理参照系 3.2.2 地图投影 3.3 地理实体分类与数据编码 3.3.1 地理实体的分类 3.3.2 地理实体的编码 3.4 空间数据的采集 3.4.1 几何数据的采集 3.4.2 属性数据的采集 3.4.3 空间数据的检核 3.5 GIS的数据质量 3.5.1 GIS数据质量的内容和类型 3.5.2 研究GIS数据质量的方法 3.5.3 数据处理中数据质量的评价 3.5.4 数据处理中数据质量的评价 3.6 空间数据标准 3.6.1 空间数据分类标准 3.6.2 空间数据交换标准 3.6.3 我国空间数据交换格式 3.6.4 GIS空间元数据 3.6.5 空间数据的互操作和Open GIS规范 参考题 第4章 空间数据的处理 4.1 矢量数据拓扑关系的自动建立 4.1.1 链的组织 4.1.2 结点匹配 4.1.3 检查多形是否闭合 4.1.4 建立多边形 …… 第5章 空间查询与空间分析 第6章 空间信息的可视化 第7章 地理信息系统的应用 第8章 地理信息系统的开发与评价 附录 主要参考文献
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值