编程入门12:Python异常处理

上一篇:编程入门11:Python面向对象

我们在编程时常常要和各种错误信息打交道,当Python解释器发现程序的错误时,就会抛出“异常”(Exception)来提示错误——这种情况可能发生于“编译时”和“运行时”这两个不同的阶段:Python程序在运行之前要先编译,如果编译未通过就不会开始运行——你可以在IPython一次交互中输入包含多条语句的程序来验证一下(按Ctrl+Enter换行,按Shift+Enter提交):

In [1]: print(2/3)
   ...: print(2///3)
   ...: print("结束")
  File "<ipython-input-1-90ecc1ce7c0b>", line 2
    print(2///3)
             ^
SyntaxError: invalid syntax


In [2]: print(2/3)
   ...: print(2/0)
   ...: print("结束")
   ...: 
0.6666666666666666
Traceback (most recent call last):

  File "<ipython-input-2-7d58b37c849b>", line 2, in <module>
    print(2/0)

ZeroDivisionError: division by zero

上面第一段程序的第二句不符合语法,编译因此中断并抛出“语法错误”(SyntaxError)异常,这就属于编译时错误;通过编译的程序在运行期间仍可能出现导致程序中止的问题——上面第二段程序在语法上没有问题,第一句也正常执行了,但第二句中除数为零的运算违反数学规则,运行因此中止并抛出“除零错误”(ZeroDivisionError)异常,这就属于运行时错误。

运行时错误难免会发生——用户输入的数据不完整、打开的文件格式不正确或连接的网络不通畅等等,都可能导致抛出异常。开发者应该预先考虑到各种异常情况,增加相应的代码来处理运行时错误以避免程序意外中止。所有异常对象都是特定异常类的实例,最基本的异常类是BaseException,通常在编程中需要处理的异常则都继承自BaseException的子类Exception。如果想要查看异常类的继承关系,可以使用mro方法返回“方法解析顺序”(Method Resolution Order)——这实际上就是类的继承顺序,一直上溯到object类为止。此外,你还可以使用raise语句直接“召唤”异常,或使用assert语句“指明”条件来触发异常。

In [3]: SyntaxError.mro()
Out[3]: [SyntaxError, Exception, BaseException, object]

In [4]: ZeroDivisionError.mro()
Out[4]: [ZeroDivisionError, ArithmeticError, Exception, BaseException, object]

In [5]: help(Exception.mro)
Help on built-in function mro:

mro(...) method of builtins.type instance
    mro() -> list
    return a type's method resolution order

In [6]: raise Exception("发生了错误")
Traceback (most recent call last):

  File "<ipython-input-6-2b67d8d306dd>", line 1, in <module>
    raise Exception("发生了错误")

Exception: 发生了错误

In [7]: a = 20
   ...: assert a <= 10, "数值过大"
   ...: 
Traceback (most recent call last):

  File "<ipython-input-7-58c21e2f5947>", line 2, in <module>
    assert a <= 10, "数值过大"

AssertionError: 数值过大

Python提供try语句来实现异常处理:“尝试”执行可能出错的代码,如有“异常”就进行相应处理——提醒用户再次输入、检查文件或重新连网等等,使程序能够顺畅地运行下去。下面我们来看一个非常简单的计算程序:根据输入的算式输出答案——使用eval函数能把字符串作为表达式来求值,但是用户可能输入不合法的表达式,导致运行时错误的发生,因此就要使用try语句来处理异常:尝试运行try代码段,如无异常则运行之后的语句,如有异常就转而执行except代码段,这样即使用户输入错误的内容,程序也不至于崩溃。

"""calc.py 简单的计算程序
"""
ans = ""
while True:
    ask = input("输入算式或回车退出:")
    if ask == "":
        break
    try:
        ans = eval(ask)
    except:
        pass
    print(ans)

以上程序中的pass语句表示什么也不做直接放过,这当然不好——正如Python之禅所言“错误不可放过”——以下程序捕获抛出的Exception类(包括其所有继承者)实例并赋值给变量e,这样就能用repr(e)返回异常类型及提示信息,通知我们具体发生了什么问题再继续运行:

    except Exception as e:
        ans = repr(e)

你可以在try语句中使用多个except子句,捕获不同类型的异常进行分别处理,例如输出自定义的提示信息。此外,你还可以添加一个finally子句,在其中编写无论是否发生异常都要“最终”执行的代码。

    except SyntaxError:
        ans = "语法不正确"
    except ZeroDivisionError:
        ans = "除数不能为零"
    except Exception as e:
        ans = "发生{}错误".format(e.__class__.__name__)
    finally:
        print("输出结果:", end="")

下面让我们来看一个在线随机图片API的测试程序:访问指定的“岁月小筑”网址会返回一张随机图片的网址,然后调用默认浏览器打开:

"""urlgetpic.py “岁月小筑”随机图片API测试程序
获取一张随机图片的网址并用浏览器打开
"""
from urllib.request import urlopen
import webbrowser
url = "http://img.xjh.me/random_img.php?return=url"


def main():
    try:
        pic = urlopen(url).read().decode()
        webbrowser.open("http:" + pic)
    except Exception as e:
        print(repr(e))


if __name__ == "__main__":
    main()
12_pic.jpg

网络总会有连不上的时候,所以访问在线资源的代码应该用try语句加以保护。

——编程原来这样……

编程小提示:开放API

API指“应用编程接口”(Application Programming Interface),其作用是让外部开发者可以调用程序的特定功能,而又无需关心程序内部的细节。在互联网时代,把网络服务封装成一系列数据接口开放出去供第三方开发者使用,这就叫做开放API——从更广泛的意义上讲,任何网站都是API,你可以编写程序从网站中获取所需要的信息,就像调用本地函数返回结果一样。

下一篇:编程入门13:Python文本处理

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 引言 在程序运行过程中(注意是运行阶段,程序可以通过编译),如果JVM检测出一个不可能执行的操作,就会出现运行时错...
    Steven1997阅读 2,518评论 1 6
  • Python 面向对象Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对...
    顺毛阅读 4,271评论 4 16
  • 八、深入理解java异常处理机制 引子try…catch…finally恐怕是大家再熟悉不过的语句了, 你的答案是...
    壹点零阅读 1,662评论 0 0
  • 有人说年轻是资本,充沛的时间和精力,打一打鸡血就能向前猛冲。也有人说,年轻就是一张白纸,什么都不是,唯有等在染缸里...
    梦鱿者阅读 4,267评论 1 8
  • 不知道第几天冥想了,观想顶轮的时候会看到自己的顶轮不再是一个圆圈什么的,而是一个皇冠,皇冠下面部分是原子运行的轨迹...
    米粒粒1阅读 233评论 2 0