python try...finally...的实现方法


Posted in Python onNovember 25, 2020

1. 关于 try.. finally..

假如上帝用 python 为每一个来到世界的生物编写程序,那么除去中间过程的种种复杂实现,最不可避免的就是要保证每个实例最后都要挂掉。代码可简写如下:

try:
  born() # 出生

  # 正常降临世界
  # do something..

except ValueError:
  # 安排错误
  # do something...

except AttributeError:
  # 特征错误
  # do something...

except TypeError:
  # 种类错误
  # do something...

... # 等等杂七杂八的错误

finally:
  go_die() # 挂掉
  come_to_see_me() # 然后来见我
  reincarnate() # 下一轮,安排!

这就是 finally 的作用和实例。就算捕获异常后再次出现异常,最终也能保证 go_die 方法会执行,但是,如果 go_die 方法出现错误,那么就不能正常去见上帝了。为了保证每个生物(不管有没有挂掉)都能见到上帝他老人家,并开始下一个轮回(不管有没有见到),需要做如下处理:

...

finally:
  try:
    go_die()
  finally:
    try:
      come_to_see_me()
    finally:
      reincarnate()

OK,功能虽然实现了,但按照 The Zen of Python 所说:Flat is better than nested.(扁平优于嵌套),那么这段代码就略显丑陋了。为了遵循 python 美学,我们可以对这段进行优化,使它看起来更为美观。

2. 错误的上下文:__context__

在此之前,需要引入一个新的概念: __context____context__ 的字面意思就是上下文,它属于错误的一个属性。在错误捕获中,它意味着当你处理一个错误时,另一个错误发生了。也就是说,你所捕获的错误虽然被成功捕获了,但当捕获完成时,你的一些操作导致另一个错误发生,而这个错误并没有被捕获。通常情况下,如果处理的好,那么当前错误的 __context__ 的值为 None,如果处理不好那就是你所捕获的错误。比如下面的代码:

def type_err():
  raise TypeError('this is a type error.')

def after_type_err():
  raise ValueError('this is a value error.')

try:
  type_err()
except TypeError:
  after_type_err()

执行结果为:

Traceback (most recent call last):
 File "<ipython-input-4-189a22d65266>", line 8, in <module>
  type_err()
 File "<ipython-input-4-189a22d65266>", line 2, in type_err
  raise TypeError('this is a type error.')
TypeError: this is a type error.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
 File "C:\Users\lineu\AppData\Local\Programs\Python\Python37\lib\site-packages\IPython\core\interactiveshell.py", line 3326, in run_code
  exec(code_obj, self.user_global_ns, self.user_ns)
 File "<ipython-input-4-189a22d65266>", line 10, in <module>
  after_type_err()
 File "<ipython-input-4-189a22d65266>", line 5, in after_type_err
  raise ValueError('this is a value error.')
ValueError: this is a value error.

在上面的错误信息中,当前错误类型为 ValueError,它的 __context__ 属性值为 TypeError 实例,而 TypeError 实例的 __context__None

3. FinalExecutor:优雅的 finally

有了 __context__ 的概念,我们就可以基于此实现一个优雅的“轮回”了。基本思路为:依次执行方法,如果方法报错,那么就将该错误的 __context__ 值设置为上一个错误(如果有)。最后等到所有方法执行完毕,再抛出最后一个错误,那么此时的错误将包含所有可能被引发的错误信息。具体代码如下:

class FinalExecutor(object):
  """终极执行器
  用于确保你所有的方法都会被执行(不管中途有没有方法报错)
  同时能看到正确的错误信息
  """

  def __init__(self):
    self.last_err = None # 保存最近发生的错误

  def __enter__(self):
    return self

  def __exit__(self, exc_type, exc_val, exc_tb):
    # 如果有发生错误,则抛出
    if self.last_err:
      raise self.last_err

  def call(self, func, *args, **kwargs):
    """调用执行方法"""
    try:
      func(*args, **kwargs)
    except Exception as e:
      # Exception 捕获所有继承自它或它子类的错误类型
      # 捕获它等于捕获几乎所有错误

      if self.last_err:
        # 将本次错误的上下文定义为上一次错误
        e.__context__ = self.last_err

      # 更新为当前错误
      self.last_err = e

我们的终极执行器使用示例为:

# 定义 3 个方法用于测试
def type_err():
  print('type error')
  raise TypeError('x')


def value_err():
  print('value error')
  raise ValueError('x')


def attr_err():
  print('attr error')
  raise AttributeError('x')


# 使用 with 语句来启动终极执行器
with FinalExecutor() as e:
  e.call(type_err)
  e.call(value_err)
  e.call(attr_err)

运行可以看到方法最终都被执行了,且错误信息一个不漏:

type error
value error
attr error
Traceback (most recent call last):
 File "<ipython-input-5-1b07c576630b>", line 19, in call
  func(*args, **kwargs)
 File "<ipython-input-6-d602d89ed0e7>", line 3, in type_err
  raise TypeError('x')
TypeError: x

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
 File "<ipython-input-5-1b07c576630b>", line 19, in call
  func(*args, **kwargs)
 File "<ipython-input-6-d602d89ed0e7>", line 8, in value_err
  raise ValueError('x')
ValueError: x

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
 File "C:\Users\lineu\AppData\Local\Programs\Python\Python37\lib\site-packages\IPython\core\interactiveshell.py", line 3326, in run_code
  exec(code_obj, self.user_global_ns, self.user_ns)
 File "<ipython-input-6-d602d89ed0e7>", line 19, in <module>
  e.call(attr_err)
 File "<ipython-input-5-1b07c576630b>", line 15, in __exit__
  raise self.last_err
 File "<ipython-input-5-1b07c576630b>", line 19, in call
  func(*args, **kwargs)
 File "<ipython-input-6-d602d89ed0e7>", line 13, in attr_err
  raise AttributeError('x')
AttributeError: x

4. 使用 ExitStack

有了我们的终极执行器,上帝就可以优雅的写代码了。为了让每个人都能这样优雅的写 python 代码,python 为我们提供了一个封装好的功能,当然它的实现要比我们的终极执行器复杂一些(考虑的也更周到一些~)。我们可以通过 contextlib 模块导入该方法并使用:

from contextlib import ExitStack


with ExitStack() as stack:
  stack.callback(type_err)
  stack.callback(value_err)
  stack.callback(attr_err)

注意该 ExitStackFinalExecutor 不同的是,它是倒序执行的。

以上就是python try...finally...的实现方法的详细内容,更多关于python try...finally的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
跟老齐学Python之通过Python连接数据库
Oct 28 Python
python类和继承用法实例
Jul 07 Python
Python实现可自定义大小的截屏功能
Jan 20 Python
pyhton列表转换为数组的实例
Apr 04 Python
Python创建一个空的dataframe,并循环赋值的方法
Nov 08 Python
python求最大值,不使用内置函数的实现方法
Jul 09 Python
Djang的model创建的字段和参数详解
Jul 27 Python
Pytorch之view及view_as使用详解
Dec 31 Python
Pytorch 实现计算分类器准确率(总分类及子分类)
Jan 18 Python
python按照list中字典的某key去重的示例代码
Oct 13 Python
linux centos 7.x 安装 python3.x 替换 python2.x的过程解析
Dec 14 Python
python3 字符串str和bytes相互转换
Mar 23 Python
通过Python pyecharts输出保存图片代码实例
Nov 25 #Python
如何基于Python和Flask编写Prometheus监控
Nov 25 #Python
python3爬虫中多线程进行解锁操作实例
Nov 25 #Python
mac系统下安装pycharm、永久激活、中文汉化详细教程
Nov 24 #Python
python 基于wx实现音乐播放
Nov 24 #Python
Python WebSocket长连接心跳与短连接的示例
Nov 24 #Python
Python 利用Entrez库筛选下载PubMed文献摘要的示例
Nov 24 #Python
You might like
PHP 文章中的远程图片采集到本地的代码
2009/07/30 PHP
php自定义apk安装包实例
2014/10/20 PHP
CI框架中site_url()和base_url()的区别
2015/01/07 PHP
CI框架文件上传类及图像处理类用法分析
2016/05/18 PHP
关于Laravel Route重定向的一个注意点
2017/01/16 PHP
Laravel框架Request、Response及Session操作示例
2019/05/06 PHP
PHP实现字母数字混合验证码功能
2019/07/11 PHP
Jquery带搜索框的下拉菜单
2013/05/06 Javascript
JavaScript验证图片类型(扩展名)的函数分享
2014/05/05 Javascript
基于JavaScript判断浏览器到底是关闭还是刷新(超准确)
2016/02/01 Javascript
JS创建对象的写法示例
2016/11/04 Javascript
JS实现给对象动态添加属性的方法
2017/01/05 Javascript
微信小程序 template模板详解及实例
2017/02/21 Javascript
jQuery判断邮箱格式对错实例代码讲解
2017/04/12 jQuery
基于vue通用表单解决方案的思考与分析
2019/03/16 Javascript
Vue实现兄弟组件间的联动效果
2020/01/21 Javascript
ckeditor一键排版功能实现方法分析
2020/02/06 Javascript
[02:39]DOTA2英雄基础教程 极限穿梭编织者
2013/12/05 DOTA
[55:45]DOTA2上海特级锦标赛D组败者赛 Liquid VS COL第一局
2016/02/28 DOTA
[02:11]2016国际邀请赛中国区预选赛最美TA采访现场玩家
2016/06/28 DOTA
使用Python的Tornado框架实现一个简单的WebQQ机器人
2015/04/24 Python
python使用socket创建tcp服务器和客户端
2018/04/12 Python
python 读取文件并替换字段的实例
2018/07/12 Python
Python面向对象之类和对象实例详解
2018/12/10 Python
python中dir()与__dict__属性的区别浅析
2018/12/10 Python
Python 20行简单实现有道在线翻译的详解
2019/05/15 Python
django settings.py 配置文件及介绍
2019/07/15 Python
python OpenCV GrabCut使用实例解析
2019/11/11 Python
wxPython实现分隔窗口
2019/11/19 Python
Python 实现PS滤镜的旋涡特效
2020/12/03 Python
pyqt5实现井字棋的示例代码
2020/12/07 Python
纯CSS3打造动感漂亮时尚的扇形菜单
2014/03/18 HTML / CSS
socket.io 和canvas 实现的共享画板功能
2019/05/22 HTML / CSS
2014民事授权委托书范本
2014/09/29 职场文书
员工离职感谢信
2015/01/22 职场文书
2019大学生暑期实习心得总结
2019/08/21 职场文书