资讯专栏INFORMATION COLUMN

SICP Python 描述 3.4 异常

pkhope / 1049人阅读

摘要:的最常见的作用是构造异常实例并抛出它。子句组只在执行过程中的异常产生时执行。每个子句指定了需要处理的异常的特定类。将强制转为字符串会得到由返回的人类可读的字符串。

3.4 异常

来源:3.4 Exceptions

译者:飞龙

协议:CC BY-NC-SA 4.0

程序员必须总是留意程序中可能出现的错误。例子数不胜数:一个函数可能不会收到它预期的信息,必需的资源可能会丢失,或者网络上的连接可能丢失。在设计系统时,程序员必须预料到可能产生的异常情况并且采取适当地措施来处理它们。

处理程序中的错误没有单一的正确方式。为提供一些持久性服务而设计的程序,例如 Web 服务器 应该对错误健壮,将它们记录到日志中为之后考虑,而且在尽可能长的时间内继续接受新的请求。另一方面,Python 解释器通过立即终止以及打印错误信息来处理错误,便于程序员在错误发生时处理它。在任何情况下,程序员必须决定程序如何对异常条件做出反应。

异常是这一节的话题,它为程序的错误处理提供了通用的机制。产生异常是一种技巧,终止程序正常执行流,发射异常情况产生的信号,并直接返回到用于响应异常情况的程序的封闭部分。Python 解释器每次在检测到语句或表达式错误时抛出异常。用户也可以使用raiseassert语句来抛出异常。

抛出异常。异常是一个对象实例,它的类直接或间接继承自BaseException类。第一章引入的assert语句产生AssertionError类的异常。通常,异常实例可以使用raise语句来抛出。raise语句的通用形式在 Python 文档中描述。raise的最常见的作用是构造异常实例并抛出它。

>>> raise Exception("An error occurred")
Traceback (most recent call last):
  File "", line 1, in 
Exception: an error occurred

当异常产生时,当前代码块的语句不会继续执行。除非异常被解决了(下面会描述),解释器会直接返回到“读取-求值-打印”交互式循环中,或者在 Python 以文件参数启动的情况下会完全终止。此外,解释器会打印栈回溯,它是结构化的文本块,描述了执行分支中的一系列嵌套的活动函数,它们是异常产生的位置。在上面的例子中,文件名称表示异常由用户在交互式会话中产生,而不是文件中的代码。

处理异常。异常可以使用封闭的try语句来处理。try语句由多个子句组成,第一个子句以try开始,剩下的以except开始。

try:
    
except  as :
    
...

try语句执行时,总是会立即执行。except子句组只在执行过程中的异常产生时执行。每个except子句指定了需要处理的异常的特定类。例如,如果AssertionError,那么任何继承自AssertionError的类实例都会被处理,标识符 绑定到所产生的异常对象上,但是这个绑定在之外并不有效。

例如,我们可以使用try语句来处理异常,在异常发生时将x绑定为0

>>> try:
        x = 1/0
    except ZeroDivisionError as e:
        print("handling a", type(e))
        x = 0
handling a 
>>> x
0

try语句能够处理产生在函数体中的异常,函数在中调用。当异常产生时,控制流会直接跳到最近的try语句的能够处理该异常类型的的主体中。

>>> def invert(x):
        result = 1/x  # Raises a ZeroDivisionError if x is 0
        print("Never printed if x is 0")
        return result
>>> def invert_safe(x):
        try:
            return invert(x)
        except ZeroDivisionError as e:
            return str(e)
>>> invert_safe(2)
Never printed if x is 0
0.5
>>> invert_safe(0)
"division by zero"

这个例子表明,invert中的print表达式永远不会求值,反之,控制流跳到了handler中的except子句组中。将ZeroDivisionError e强制转为字符串会得到由handler: "division by zero"返回的人类可读的字符串。

3.4.1 异常对象

异常对象本身就带有属性,例如在assert语句中的错误信息,以及有关异常产生处的信息。用户定义的异常类可以携带额外的属性。

在第一章中,我们实现了牛顿法来寻找任何函数的零点。下面的例子定义了一个异常类,无论何时ValueError出现,它都返回迭代改进过程中所发现的最佳猜测值。数学错误(ValueError的一种)在sqrt在负数上调用时产生。这个异常由抛出IterImproveError处理,它将牛顿迭代法的最新猜测值储存为参数。

首先,我们定义了新的类,继承自Exception

>>> class IterImproveError(Exception):
        def __init__(self, last_guess):
            self.last_guess = last_guess

下面,我们定义了IterImprove,我们的通用迭代改进算法的一个版本。这个版本通过抛出IterImproveError异常,储存最新的猜测值来处理任何ValueError。像之前一样,iter_improve接受两个函数作为参数,每个函数都接受单一的数值参数。update函数返回新的猜测值,而done函数返回布尔值,表明改进是否收敛到了正确的值。

>>> def iter_improve(update, done, guess=1, max_updates=1000):
        k = 0
        try:
            while not done(guess) and k < max_updates:
                guess = update(guess)
                k = k + 1
            return guess
        except ValueError:
            raise IterImproveError(guess)

最后,我们定义了find_root,它返回iter_improve的结果。iter_improve应用于由newton_update返回的牛顿更新函数。newton_update定义在第一章,在这个例子中无需任何改变。find_root的这个版本通过返回它的最后一个猜测之来处理IterImproveError

>>> def find_root(f, guess=1):
        def done(x):
            return f(x) == 0
        try:
            return iter_improve(newton_update(f), done, guess)
        except IterImproveError as e:
            return e.last_guess

考虑使用find_root来寻找2 * x ** 2 + sqrt(x)的零点。这个函数的一个零点是0,但是在任何负数上求解它会产生ValueError。我们第一章的牛顿法实现会产生异常,并且不能返回任何零点的猜测值。我们的修订版实现在错误之前返回了最新的猜测值。

>>> from math import sqrt
>>> find_root(lambda x: 2*x*x + sqrt(x))
-0.030211203830201594

虽然这个近似值仍旧距离正确的答案0很远,一些应用更倾向于这个近似值而不是ValueError

异常是另一个技巧,帮助我们将程序细节划分为模块化的部分。在这个例子中,Python 的异常机制允许我们分离迭代改进的逻辑,它在try子句组中没有发生改变,以及错误处理的逻辑,它出现在except子句中。我们也会发现,异常在使用 Python 实现解释器时是个非常实用的特性。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/38159.html

相关文章

  • SICP Python 描述 3.5 组合语言的解释器

    摘要:计算器语言解释器的核心是叫做的递归函数,它会求解树形表达式对象。到目前为止,我们在描述求值过程中所引用的表达式树,还是概念上的实体。解析器实际上由两个组件组成,词法分析器和语法分析器。标记序列由叫做的词法分析器产生,并被叫做语法分析器使用。 3.5 组合语言的解释器 来源:3.5 Interpreters for Languages with Combination 译者:飞龙 ...

    sanyang 评论0 收藏0
  • SICP Python 描述 第三章 计算机程序的构造和解释 3.1 引言

    摘要:为通用语言设计解释器的想法可能令人畏惧。但是,典型的解释器拥有简洁的通用结构两个可变的递归函数,第一个求解环境中的表达式,第二个在参数上调用函数。这一章接下来的两节专注于递归函数和数据结构,它们是理解解释器设计的基础。 3.1 引言 来源:3.1 Introduction 译者:飞龙 协议:CC BY-NC-SA 4.0 第一章和第二章描述了编程的两个基本元素:数据和函数之间的...

    v1 评论0 收藏0
  • SICP Python 描述 1.4 实践指南:函数的艺术

    摘要:实践指南函数的艺术来源译者飞龙协议函数是所有程序的要素,无论规模大小,并且在编程语言中作为我们表达计算过程的主要媒介。目前为止,我们讨论了函数的形式特性,以及它们如何使用。第一行描述函数的任务。 1.4 实践指南:函数的艺术 来源:1.4 Practical Guidance: The Art of the Function 译者:飞龙 协议:CC BY-NC-SA 4.0 函...

    lemon 评论0 收藏0
  • SICP Python 描述 第二章 使用对象构建抽象 2.1 引言

    摘要:对象表示信息,但是同时和它们所表示的抽象概念行为一致。通过绑定行为和信息,对象提供了可靠独立的日期抽象。名称来源于实数在中表示的方式浮点表示。另一方面,对象可以表示很大范围内的分数,但是不能表示所有有理数。 2.1 引言 来源:2.1 Introduction 译者:飞龙 协议:CC BY-NC-SA 4.0 在第一章中,我们专注于计算过程,以及程序设计中函数的作用。我们看到了...

    phoenixsky 评论0 收藏0
  • SICP Python 描述 第五章 序列和协程

    摘要:消息向迭代器获取所表示的底层序列的下一个元素。为了对方法调用做出回应,迭代器可以执行任何计算来获取或计算底层数据序列的下一个元素。这个迭代器应拥有方法,依次返回序列中的每个元素,最后到达序列末尾时产生异常。 第五章 序列和协程 来源:Chapter 5: Sequences and Coroutines 译者:飞龙 协议:CC BY-NC-SA 4.0 5.1 引言 在这一章中,我...

    leap_frog 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<