异常处理,是编程语言或计算机硬件里的一种机制,用于处理软件或信息系统中出现的异常情况(即超出程序正常执行流程的某些特殊条件)。Python和R做为一门编程语言天然也是有各自的异常处理机制的,异常处理机制在代码编写中扮演着很是关键的角色,却又是许多人容易混淆的地方。对于异常机制的合理运用是直接关系到码农饭碗的事情!因此,本文将具体介绍一下Python和R的异常处理机制,阐明两者在异常处理机制上的异同。html
在了解Python和R的异常机制以前,咱们有必要了解一下异常安全
的概念。python
根据WikiPedia的文献,一段代码是异常安全的,若是这段代码运行时的失败不会产生有害后果,如内存泄露、存储数据混淆、或无效的输出。咱们能够知道一段代码的异常安全一般分为下面五类:git
异常安全一般分为5个层次:github
失败透明:若是出现了异常,将不会对外进一步抛出该异常。(通常比较复杂)数据库
强异常安全:能够运行失败,不过数据会回滚到代码运行前(无反作用)编程
基本异常安全:运行失败致使的数据变动,使得代码运行先后数据不一致了(有反作用)segmentfault
最小异常安全:运行失败保存了无效数据,可是还不会引发崩溃,资源不会泄露(进程不会挂)数组
异常不安全:没有任何保证(进程可能会挂掉)缓存
从上述的5个层次来看,咱们能够知道,在平时写代码的时候,对数据库、文件、网络等的IO操做都是须要尽可能保证无反作用的,也就是强异常安全。具体来讲就是,RDBS操做在失败的时候须要回滚机制、全部IO操做在最后要保证IO链接资源关闭。安全
其实和多数语言的异常机制的语法是相似的:Python和R都是经过抛出一个异常对象或一个枚举类的值来返回一个异常;异常处理代码的做用域由try开始,以第一个异常处理子句(catch, except等)结束;可连续出现若干个异常处理子句,每一个处理特定类型的异常。最后经过finally子句,不管是否出现异常它都将执行,用于释放异常处理所需的一些资源。
下面将具体介绍两者的异常处理机制。
首先,Python 是一门面向对象语言,全部的异常类都是经过继承BaseException
类来实现的,咱们亦能够经过相应的继承来实现自定义的异常类,好比在工做流调度中使用AirflowException
,具体实现能够直接看Airflow的源码。
事实上,这些在咱们代码处理范围内的异常其实就是能够分红两个部分:
IO异常:由网络抖动、磁盘文件位置变动、数据库链接变动等引发的IO异常问题。
运行期异常:因为计算或者传输的参数参数类型有误、参数值异常等等发生在运行期的异常,都统一被称为运行期异常。正常来讲,IO上的异常咱们都要有相应的try-catch-finally
机制,在Python也就是以下实现:
try: do something with IO except: do something without IO finally: close IO
这里容易犯的一个错误就是在except
中又引入了新的IO操做,好比在except
中又引入了一个API的POST请求或者数据库写操做等等,这样若是在except
阶段又发生了异常,将致使异常信息的丢失。
另外一方面,对于可能的运行期异常则须要咱们根据具体应用场景的需求来作相应的处理,通常就是遇到一个新的问题加一个新的异常捕获机制,固然这里也就考验到码农程序设计的功利,是否可以未雨绸缪。好比数组长度的检查,传入字典的Key检查等等。Python自己提供了丰富的异常处理类型而且易于拓展,正确使用将能够显著提高程序的鲁棒性(保住码农的饭碗)。
使用try-catch-finally
机制是足够简单的,可是在混入return
和rasie
操做以后,事情就看起来变得有点复杂。
举一个例子:
def test(): try: a = 1/0 except: a = 0 raise(ValueError,"value error, the division must greater than 0") return a finally: a = 1 return a test()
你看这里的返回应该是什么呢?
其实,这里的返回最后应该是 1,而except中raise的异常则会被吃掉
。这也是许多人错误使用finanlly的一个很好的例子。
Python在执行带有fianlly
的子句时会将except
内抛出的对象先缓存起来,优先执行finally
中抛出的对象,若是finally
中先抛出了return
或者raise
,那么except
段抛出的对象将看起来被吃掉
了。
一个段正确的处理方式应该是这样的:
try: do IO info = {"status":200} except: info = {"status":400} finally: try: write log(info) except: raise(SomeError,"error message") close IO
具体的调用栈的过程能够参考这个更加生动的例子:
R和Python最大的不一样就是 R 本质上是一门强动态类型的非纯函数式编程语言(所谓非纯即存在反作用)而非面向对象语言。从函数式编程语言的角度上讲,R和Erlang、LISP的关系比较近一些。
既然是函数式语言,处理异常也是经过函数式的,而非直接经过面向对象的方式。R 从语法上来看就略显突兀(花括号函数式语言的一大通病):
tryCatch({ doStuff() doMoreStuff() }, some_exception = function(se) { recover(se) })
若是这段用Python来表达就变成:
try: doStuff() doMoreStuff() except SomeException, se: recover(se)
事实上正确运用 R 的异常处理机制反而是比较负担小的一种方式:(R 还支持用中文字符集命名变量)
tryCatch({ 结果 <- 表达式 }, warning = function(w) { warning() ... # 运行期异常 }, error = function(e) { stop() ... # IO异常 }, finally { on.exit() ... # 资源回收 }
One of R’s great features is its condition system. It serves a similar purpose to the exception handling systems in Java, Python, and C++ but is more flexible. In fact, its flexibility extends beyond error handling–conditions are more general than exceptions in that a condition can represent any occurrence during a program’s execution that may be of interest to code at different levels on the call stack. For example, in the section “Other Uses for Conditions,” you’ll see that conditions can be used to emit warnings without disrupting execution of the code that emits the warning while allowing code higher on the call stack to control whether the warning message is printed. For the time being, however, I’ll focus on error handling.
The condition system is more flexible than exception systems because instead of providing a two-part division between the code that signals an error and the code that handles it, the condition system splits the responsibilities into three parts–signaling a condition, handling it, and restarting. In this chapter, I’ll describe how you could use conditions in part of a hypothetical application for analyzing log files. You’ll see how you could use the condition system to allow a low-level function to detect a problem while parsing a log file and signal an error, to allow mid-level code to provide several possible ways of recovering from such an error, and to allow code at the highest level of the application to define a policy for choosing which recovery strategy to use.
个人理解是R经过条件机制,然咱们能够选择性的在低阶函数中把warning
吃掉,这样就不至于影响高阶函数的运行?条件机制将异常分为三阶段而不是两阶段:
1.异常信号捕获
2.异常处理
3.重启机制。
而且咱们还能够看到在异常处理中,如何在中阶函数中恢复低阶函数的Error
,而且在高阶函数中选择必定的恢复策略。
这段貌似我的理解有误,还请看官指正。
更优阅读体验可直接访问原文地址:https://segmentfault.com/a/11...
做为分享主义者(sharism),本人全部互联网发布的图文均听从CC版权,转载请保留做者信息并注明做者 Harry Zhu 的 FinanceR专栏:https://segmentfault.com/blog...,若是涉及源代码请注明GitHub地址:https://github.com/harryprince。微信号: harryzhustudio商业使用请联系做者。