python动态捕获异常-乾颐堂

在讨论动态捕获异常时让我大吃一惊的是,可以让我找到隐藏的Bug和乐趣...

有问题的代码

下面的代码来自一个产品中看起来是好的抽象代码 - slightly(!) .这是调用一些统计数据的函数,然后进行处理 . 首先是用socket连接获取一个值,可能发生了socket错误.由于统计数据在系统中不是至关重要的,我们只是记一下日志错误并继续往下走.

(请注意,这篇文章我使用doctest测试的 - 这代表代码可以运行!)

>>> def get_stats():

...     pass

...

>>> def do_something_with_stats(stats):

...     pass

...

>>> try:

...     stats = get_stats()

... except socket.error:

...     logging.warning("Can‘t get statistics")

... else:

...     do_something_with_stats(stats)

查找

我们测试时并没有发现不妥, 但实际上我们注意到静态分析报告显示一个问题:

$ flake8 filename.py

filename.py:351:1: F821 undefined name ‘socket‘

filename.py:352:1: F821 undefined name ‘logging‘

显然是我们没测试,这个问题是代码中我们没有引用socket 和 logging 两个模块.使我感到惊奇的是,这并没有预先抛出NameError错,我以为它会查找这些异常语句中的一些名词,如它需要捕捉这些异常,它需要知道些什么呢!

事实证明并非如此,异常语句的查找是延迟完成的,只是评估时抛出异常. 不只是名称延迟查找,也可以定制显示声明异常做为‘参数(argument)‘.

这可能是好事,坏事,或者是令人厌恶的.

好事(上段中提到的)

异常参数可以以任意形式数值传递. 这样就允许了异常的动态参数被捕获.

>>> def do_something():

...    blob

...

>>> def attempt(action, ignore_spec):

...     try:

...         action()

...     except ignore_spec:

...         pass

...

>>> attempt(do_something, ignore_spec=(NameError, TypeError))

>>> attempt(do_something, ignore_spec=TypeError)

Traceback (most recent call last):

...

NameError: global name ‘blob‘ is not defined

坏事(上段中提到的)

这种明显的弊端就是异常参数中的错误通常只有在异常触发之后才会被注意到,不过为时已晚.当用异常去捕获不常见的事件时(例如:以写方式打开文件失败),除非做个一个特定的测试用例,否则只有当一个异常(或者任何异常)被触发的时候才会知道, 届时记录下来并且查看是否有匹配的异常, 并且抛出它自己的错误异常 - 这是一个NameError通常所做的事情.

>>> def do_something():

...     return 1, 2

...

>>> try:

...     a, b = do_something()

... except ValuError:  # oops - someone can‘t type

...     print("Oops")

... else:

...     print("OK!")   # we are ‘ok‘ until do_something returns a triple...

OK!

令人讨厌的(上段中提到的)

>>> try:

...    TypeError = ZeroDivisionError  # now why would we do this...?!

...    1 / 0

... except TypeError:

...    print("Caught!")

... else:

...    print("ok")

...

Caught!

不仅仅是异常参数通过名称查找, - 其它的表达式也是这样工作的:

>>> try:

...     1 / 0

... except eval(‘‘.join(‘Zero Division Error‘.split())):

...     print("Caught!")

... else:

...     print("ok")

...

Caught!

异常参数不仅仅只能在运行时确定,它甚至可以使用在生命周期内的异常的信息. 以下是一个比较费解的方式来捕捉抛出的异常 - 但也只能如此了:

>>> import sys

>>> def current_exc_type():

...     return sys.exc_info()[0]

...

>>> try:

...     blob

... except current_exc_type():

...     print ("Got you!")

...

Got you!

很明显这才是我们真正要寻找的当我们写异常处理程序时, 我们应该首先想到的就是这种

(字节)代码

为了确认它是如何在异常处理工作中出现的,我在一个异常的例子中运行 dis.dis(). (注意 这里的分解是在Python2.7 下 - 不同的字节码是Python 3.3下产生的,但这基本上是类似的):

>>> import dis

>>> def x():

...     try:

...         pass

...     except Blobbity:

...         print("bad")

...     else:

...         print("good")

...

>>> dis.dis(x)  # doctest: +NORMALIZE_WHITESPACE

2           0 SETUP_EXCEPT             4 (to 7)

<BLANKLINE>

3           3 POP_BLOCK

4 JUMP_FORWARD            22 (to 29)

<BLANKLINE>

4     >>    7 DUP_TOP

8 LOAD_GLOBAL              0 (Blobbity)

11 COMPARE_OP              10 (exception match)

14 POP_JUMP_IF_FALSE       28

17 POP_TOP

18 POP_TOP

19 POP_TOP

<BLANKLINE>

5          20 LOAD_CONST               1 (‘bad‘)

23 PRINT_ITEM

24 PRINT_NEWLINE

25 JUMP_FORWARD             6 (to 34)

>>   28 END_FINALLY

<BLANKLINE>

7     >>   29 LOAD_CONST               2 (‘good‘)

32 PRINT_ITEM

33 PRINT_NEWLINE

>>   34 LOAD_CONST               0 (None)

37 RETURN_VALUE

这显示出了我原来预期的问题(issue). 异常处理"看起来"完全是按照Python内部机制在运行. 这一步完全没有必要知道关于后续的异常“捕获”语句, 并且如果没有异常抛出它们将被完全忽略了.SETUP_EXCEPT并不关心发生了什么, 仅仅是如果发生了异常, 第一个处理程序应该被评估,然后第二个,以此类推.

每个处理程序都有两部分组成: 获得一个异常的规则, 和刚刚抛出的异常进行对比. 一切都是延迟的, 一切看起来正如对你的逐行的代码的预期一样, 从解释器的角度来考虑. 没有任务聪明的事情发生了,只是突然使得它看起来非常聪明.

总结

虽然这种动态的异常参数让我大吃一惊, 但是这当中包含很多有趣的应用. 当然去实现它们当中的许多或许是个馊主意,呵呵

有时并不能总是凭直觉来确认有多少Python特性的支持 - 例如 在类作用域内 表达式和声明都是被显式接受的, (而不是函数, 方法, 全局作用域),但是并不是所有的都是如此灵活的. 虽然(我认为)那将是十分美好的, 表达式被禁止应用于装饰器 - 以下是Python语法错误:

@(lambda fn: fn)

def x():

pass

这个是尝试动态异常参数通过给定类型传递给第一个异常的例子, 静静的忍受重复的异常:

>>> class Pushover(object):

...     exc_spec = set()

...

...     def attempt(self, action):

...         try:

...             return action()

...         except tuple(self.exc_spec):

...             pass

...         except BaseException as e:

...             self.exc_spec.add(e.__class__)

...             raise

...

>>> pushover = Pushover()

>>>

>>> for _ in range(4):

...     try:

...         pushover.attempt(lambda: 1 / 0)

...     except:

...         print ("Boo")

...     else:

...         print ("Yay!")

Boo

Yay!

Yay!

Yay!

www.qytang.com/
http://www.qytang.com/cn/list/29/
http://www.qytang.com/cn/list/28/358.htm
http://www.qytang.com/cn/list/41/
http://www.qytang.com/cn/list/37/
http://www.qytang.com/cn/list/46/
http://www.qytang.com/cn/page/19.htm
http://www.qytang.com/cn/list/32/
http://www.qytang.com/cn/list/28/
http://www.qytang.com/cn/list/25/
http://www.qytang.com/cn/list/28/625.htm
http://www.qytang.com/cn/list/28/612.htm
http://www.qytang.com/cn/list/28/611.htm

时间: 2024-10-29 19:05:35

python动态捕获异常-乾颐堂的相关文章

python性能测试脚本-乾颐堂

废话不多说,直接上代码. import httplib import urllib import time import json    class Transaction(object):                def __init__(self):         self.custom_timers = {}        def run(self):         conn = httplib.HTTPConnection("localhost:8080")     

乾颐堂既有老腊肉也有小鲜肉,欢迎大家来学习,大学生如何学习HCIE,答案来咯

前边给大家分享了工作20余年的刘大哥的经历,下面给大家带来20出头没毕业小伙子的在乾颐堂的学习经历 9月4号开学第一天收到了一份特别的礼物,PASS HCIE 在HCIE的路上要一步一个脚印走过来,我从去年暑假之前加入乾颐堂这个大家庭,与很多在路上的HCIE一样,从对TCP/IP的一无所知,到现在MPLE.BGP.组播全都能够深入掌握,这个和乾颐堂是分不开的. 刚到乾颐堂学习的时候,直接是暑假在光大会展脱产,本来我是学生,其余的时候还要学习别的东西,所以只有暑假有充裕的时间要把握住.2个月的时间

乾颐堂安德华为数通HCNA真题解析版(第2部分)

HCNA真题解析视频即将上线,敬请关注本博客以及乾颐堂官网书接上文:16 Interface GigabitEthernet0/0/1 Port link‐type trunk Port trunk allow‐pass vlan 2 to 4094 根据如上所示的命令输出,下列描述中正确的是()(多选)A GigabitEthernet0/0/1 不允许 VLAN1 通过B GigabitEthernet0/0/1 允许 VLAN1 通过C 如果要把 GigabitEthernet0/0/1

全网唯一华为无线HCNA真题解析版题库,来自乾颐堂安德

本部分内容来自互联网.通过它,您可以同哦过华为认证HCNA-WLAN无线(H12-311)考试.后续视频讲解会上传至51CTO学院,其他学员可以通过乾颐堂官方咨询或者群645866695咨询解析版是军哥一字一字码出来的,转载请注明出处 QUESTION 1无线网络的初步应用开始于哪段时间()? A. 一次大战期间 B. 二次大战期间 C. 20世纪后期D. 2000年以后 Correct Answer: B Explanation/Reference:2战后才初步发展,战争很多时候是促进科技进步

乾颐堂安德HCIE面试真题系列20(董XG),一个失败的案例

20180720董XG杭州面试 1.ppp2.割接3.ospf中邻居建立不起来的原因 上面也是我回答问题的顺序. ppp我就按照我之前模拟面试的时候,按照题库里面往下背,这个我之前也复习到了,就在今天火车上,我还看到了,这题挺顺利,答了大概30分钟吧,中途提问过一句,就是chap认证的用户名为空的时候怎么回事和有用户名的时候分别解释下,她不问的话,我也会说的,正好她也问了,这题我感觉满分. 割接,很久之前预习过,近期也没怎么看,近期就准备理论了,就边回忆题库里面的东西边说了自己工作中碰到的机房迁

来看看军哥对HCIEv3.0预测对了多少内容吧!乾颐堂安德HCIEv3.0视频已经在线

2018年6月,我预期华为会升级数通HCIE,故而一步步的改进华为的课程体系,果不其然,华为在2018年8月末发布"预"公告,大约会在2019年1月份升级RS HCIE到3.0,一切尽在掌握之中.乾颐堂安德2018版HCIEv3.0已经开始上传51CTO它的改变按照官方原文是"HCIE-Routing&Switching V3.0的内容包括但不限于:L2及L3层网络技术.IPv4及IPv6网络协议.路由控制技术.MPLS及MPLS L3 ×××技术.组播技术.网络安全

从其他机构转投乾颐堂的同学pass感言

首先能够通过考试要感谢乾颐堂这个平台,感谢两位老师(军哥和老白),还有一起备考的兄弟.我是从其他机构过来的,之前通过了lab,一面挂了之后来了乾颐堂.首先乾颐堂的模拟面试是我通过考试的重要原因,能够真实的模拟考试环境,锻炼自己的临场表现,理清答题的思路,说白了就是考试的彩排.两道题是确定的,lab和项目,只要这两道题答好,时间说够了,理论只要了解一些,通过是没有问题的.我理论偏弱也没怎么准备,总觉的运气好,考的都会,答的都对,能够通过纯属侥幸,希望后面备考的兄弟可以不留死角,认真准备.下面我简单

乾颐堂鹏同学通过HCIE送给后来者的话

HCIE备考分享最先在考CCIE还是HCIE的时候,还是很犹豫的,CCIE至少都要到北京考试,并且我之前也考过CCNP的,感觉还是有些基础的,但是考试的成本会增加,HCIE在成都就可以考,路途等各种成本基本不用,听说面试比较难.综合考虑了一下,最终还是选择了考HCIE(想挑战一下自己).决定好以后,就开始准备考试,笔试很简单,题库刷了几遍就OK了,只要背好了,一般都是高分通过的.接下来就是备考LAB了,LAB的建议大家按照老师的方法先敲熟悉后,实现了盲敲以后(我当时都不用看拓扑就直接敲命令了),

神圣教师节再现某吹牛机构白嫖事件,乾颐堂军哥实名DISS丫的

神圣教师节再现某吹牛机构白嫖事件,我,乾颐堂军哥实名DISS这个SB机构,(注意此处的SB不是骂人,而是不良机构的首字母简写,自己脑补吧).本来高高兴兴的教师节,还收到了学员送的礼物可晚上,正在上课时候,思博白嫖事件发生了作为教书育人的某SB机构,我呵呵了,原来他们不是第一次想白嫖人家东西了,洋洋互联网真是给我们留下了不可磨灭的证据,此处给出3例,还有一些一时没有找到,果然不良果然不良啊白嫖我乾颐堂达叔的文档,大图呈上,我谢谢你为我们广而告之,但估计您初心不是如此.郑重声明:您不用白嫖了,我会把