首页 云服务器Linux编程正文

linux编程_Python异常处置的哲学

admin Linux编程 2020-05-26 11:15:38 94 0 Linux编程

所谓异常指的是程序的执行泛起了非预期行为,就好比现实中的做一件事历程中总会泛起一些意外的事。异常的处置是跨越编程语言的,和详细的编程细节相比,程序执行异常的处置更像是哲学。限于认知能力和履历所限,不可能到达像注释器下import this看到的python设计之禅一样,本文就连系实际使用简朴的聊一聊。

0. 前言

事情中,程序员之间一言不合就亮代码,究竟不管是代码自己照样其执行历程,不会存在二义性,更不会含糊不清,代码可谓是程序员之间的官方语言。然则其处置问题的逻辑或者算规则并非如此。

让我至今念念不忘的两次程序员论剑有:

反问一:项目后期所有的异常处置都要去掉,不允许上线后泛起未知的异常,把你这里的异常处置去掉,换成if else;

反问二:这里为什么要举行异常处置?代码都是你写的,怎么会泛起异常呢?

这是我亲身经历的,不知道人人碰着这两个问题会怎样回覆,至少我那时竟无言以对。这两个问题划分在差别的时间针对差别的问题出自一个互联网巨头中某个资深QA和资深开发的反问。

暂且岂论对错,究竟差别人思量问题的起点是差别的。然则从这么坚决的去异常处置的回覆中至少有一点可以一定,那就是很多人对自己的代码太过自信或者说是察觉代码潜在问题的直觉力不够,更别提准确的处置潜在的问题以保证主要营业逻辑的处置流程。写代码的时刻若是只简朴思量正常的情形,那是在往代码中下毒。

接下类本篇博文将凭据套路出牌(制止被Ctrl + W),先容一下python的异常处置的观点和详细操作.

1. 为什么要异常处置

常见的程序bug无非就两大类:

  • 语法错误;
  • 逻辑不严谨或者思维混乱导致的逻辑错误;

显然第二种错误更难被发现,且效果往往更严重。无论哪一种bug,有两种效果等着我们:一、程序崩掉;二、执行效果不符合预期;

对于一些主要关键的执行操作,异常处置可以控制程序在可控的局限执行,固然条件是准确的处置。

好比我们给第三方提供的API或者使用第三方提供的API。多数情形下要准确的处置挪用者错误的挪用参数和返回异常效果的情形,否则就可能要背黑锅了。

在不可控的环境中运行程序,异常处置是必须的。然而难题的地方是当异常发生时,若何举行处置。

2. python异常处置

下面逐步先容一下python异常处置相关的观点。

2.1 异常处置结构

需要的结构为try ... except,至少有一个except,else 和 finally 可选。

try:
    code blocks
except (Exception Class1, Exception Class2, ...) as e:
    catch and process exception
except Exception ClassN:
    catch and process exception
... ...
else:
    when nothing unexpected happened
finally:
    always executed when all to end

2.2 python 内置异常类型

模块exceptions中包罗了所有内置异常类型,类型的继续关系如下:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StandardError
      |    +-- BufferError
      |    +-- ArithmeticError
      |    |    +-- FloatingPointError
      |    |    +-- OverflowError
      |    |    +-- ZeroDivisionError
      |    +-- AssertionError
      |    +-- AttributeError
      |    +-- EnvironmentError
      |    |    +-- IOError
      |    |    +-- OSError
      |    |        +-- windowsError (Windows)
      |    |        +-- VMSError (VMS)
      |    +-- EOFError
      |    +-- ImportError
      |    +-- LookupError
      |    |    +-- IndexError
      |    |    +-- KeyError
      |    +-- MemoryError
      |    +-- NameError
      |    |    +-- UnboundLocalError
      |    +-- ReferenceError
      |    +-- RuntimeError
      |    |    +-- NotImplementedError
      |    +-- SyntaxError
      |    |    +-- IndentationError
      |    |        +-- TabError
      |    +-- SystemError
      |    +-- TypeError
      |    +-- ValueError
      |        +-- UnicodeError
      |              +-- UnicodedecodeError
      |              +-- UnicodeEncodeError
      |              +-- UnicodeTranslateError
      +-- Warning
          +-- DeprecationWarning
          +-- PendingDeprecationWarning
          +-- RuntimeWarning
          +-- SyntaxWarning
          +-- UserWarning
          +-- FutureWarning
      +-- ImportWarning
      +-- UnicodeWarning
      +-- BytesWarning

2.3 except clause

excpet子句的常用的写法如下:

  • except:                         # 默认捕捉所有类型的异常
  • except Exception Class:                   # 捕捉Exception Class类型的异常
  • except Exception Class as e:                 # 捕捉Exception Class类型的异常,异常工具赋值到e
  • except (Exception Class1, Exception Class2, ...) as e:      # 捕捉列表中随便一种异常类型

上面的异常类可以是下面python内置异常类型,也可以是自界说的异常类型。

2.4 异常匹配原则

  • 所有except子句按顺序逐一匹配,匹配乐成则忽略后续的except子句;
  • 若抛出异常工具为except子句中给出的异常类型的工具或给出的异常类型的派生类工具,则匹配乐成;
  • 若是所有的except子句均匹配失败,异常会向上通报;
  • 若是依然没有被任何try...except捕捉到,程序在终止前会挪用sys.excepthook举行处置;

2.5 else & finally

若是没有异常发生,且存在else子句,则执行else子句。只要存在finally子句,无论任何情形下都市被执行。

可能唯一欠好明白的地方就是finally。没有异常、捕捉异常、异常上传以及异常处置历程中发生异常等均会执行finally语句。

下面看个例子:

def division(a, b):
    try:
        print'res = %s' % (a / b)
    except (ZeroDivisionError, ArithmeticError) as e:
        return str(e)  # 注重此处使用的是return
    else:
        print '%s / %s = %s' % (a, b, a / b)
    finally:
        print 'finally clause'

 划分输入参数(1, 2),(1, 0)和 (1,“0”)执行:

print 'return value: %s' % division(a, b)

获得的效果如下:

res = 0
/ 2 = 0
finally clause
return value: None

finally clause
return value: integer division or modulo by zero

finally clause
Traceback (most recent call last):
  File "D:\My Folders\Cnblogs\Alpha Panda\Main.py", line 217, in <module>
    print 'return value: %s' % division(1, "0")
  File "D:\My Folders\Cnblogs\Alpha Panda\Main.py", line 208, in division
    print'res = %s' % (a / b)
TypeError: unsupported operand type(s) for /: 'int' and 'str'

可以看到纵使程序发生异常且没有被准确处置,在程序终止前,finally语句依旧被执行了。可以将此看做程序平安的最后一道有用屏障。主要举行一些善后清算事情,好比资源释放、断开网络连接等。固然with声明可以自动帮我们举行一些清算事情。

2.6 raise抛出异常

程序执行历程中可以使用raise自动的抛出异常. 

try:
    e = Exception('Hello', 'World')
    e.message = 'Ni Hao!'
    raise e
except Exception as inst:
    print type(inst), inst, inst.args, inst.message

效果:<type 'exceptions.Exception'> ('Hello', 'World') ('Hello', 'World') Ni Hao!

上面展示了except工具的属性args, message。

2.7 自界说异常

绝大部分情形下内置类型的异常已经能够知足平时的开发使用,若是想要自界说异常类型,可以直接继续内置类型来实现。

class ZeroDivZeroError(ZeroDivisionError):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self)
    def __repr__(self):
        return self.value

try:
    # do something and find 0 / 0
    raise ZeroDivZeroError('hahajun')
except ZeroDivZeroError as err:
    print 'except info %s' % err

自界说异常应该直接继续自Exception类或其子类,而不要继续自BaseException.

3. Stack Trace

python执行历程中发生异常,会告诉我们到底那里泛起问题和什么问题。这两种类型的错误信息划分为stack trace和 exception,在程序中划分用traceback object和异常工具示意。

Traceback (most recent call last):
  File "D:\My Folders\Cnblogs\Alpha Panda\Main.py", line 270, in <module>
    1 / 0
ZeroDivisionError: integer division or modulo by zero

上面的错误信息包罗错误发生时当前的客栈信息(stack trace, 前三行)和异常信息(exception,最后一行),划分存放在traceback objects和抛出的异常工具中。

异常工具及异常信息前面已经先容过,接下来我们在看一下异常发生时,stack trace的处置。

Traceback objects represent a stack trace of an exception. A traceback object is created when an exception occurs.

这时有两种情形:

  1. 异常被try...except捕捉
  2. 没有被捕捉或者爽性没有处置

正常的代码执行历程,可以使用traceback.print_stack()输出当前挪用历程的客栈信息。

3.1 捕捉异常

 对于第一种情形可以使用下面两种方式获取stack trace信息:

trace_str = traceback.format_exc()
或者从sys.exc_info()中获取捕捉的异常工具等的信息,然后花样化成trace信息。

def get_trace_str(self):
    """
    从当前栈帧或者之前的栈帧中获取被except捕捉的异常信息;
    没有被try except捕捉的异常会直接通报给sys.excepthook
    """
    t, v, tb = sys.exc_info()
    trace_info_list = traceback.format_exception(t, v, tb)
    trace_str = ' '.join(trace_info_list)

至于抛出的包罗异常信息的异常工具则可以在try...except结构中的except Exception class as e中获取。 

3.2 未捕捉异常

第二种情形,若是异常没有被处置或者未被捕捉则会在程序推出前挪用sys.excepthook将traceback和异常信息输出到sys.stderr。

def except_hook_func(tp, val, tb):
    trace_info_list = traceback.format_exception(tp, val, tb)
    trace_str = ' '.join(trace_info_list)
    print 'sys.excepthook'
    print trace_str
sys.excepthook = except_hook_func

上面自界说except hook函数来取代sys.excepthook函数。在hook函数中凭据异常类型tp、异常值和traceback工具tb获取stack trace。这种情形下不能从sys.exc_info中获取异常信息。

3.3 测试

def except_hook_func(tp, val, tb):
    trace_info_list = traceback.format_exception(tp, val, tb)
    trace_str = ' '.join(trace_info_list)
    print 'sys.excepthook'
    print trace_str
sys.excepthook = except_hook_func
try:
    1 / 0
except TypeError as e:
    res = traceback.format_exc()
    print "try...except"
    print str(e.message)
    print res

走的是sys.excepthook处置流程效果:

sys.excepthook
Traceback (most recent call last):
   File "D:\My Folders\Cnblogs\Alpha Panda\Main.py", line 259, in <module>
    1 / 0
 ZeroDivisionError: integer division or modulo by zero

将except TypeError as e 改为 except ZeroDivisionError as e,则走的是try...except捕捉异常流程,效果如下:

try...except
integer division or modulo by zero
Traceback (most recent call last):
  File "D:\My Folders\Cnblogs\Alpha Panda\Main.py", line 259, in <module>
    1 / 0
ZeroDivisionError: integer division or modulo by zero

4. 异常信息网络

讲了这么多,我们看一下若何实现一个程序中trace信息的网络。

class TracebackMgr(object):

    def _get_format_trace_str(self, t, v, tb):
        _trace = traceback.format_exception(t, v, tb)
        return ' '.join(_trace)

    def handle_one_exception(self):
        """
        从当前栈帧或者之前的栈帧中获取被except捕捉的异常信息;
        没有被try except捕捉的异常会自动使用handle_traceback举行网络
        """
        t, v, tb = sys.exc_info()
        self.handle_traceback(t, v, tb, False)

    def handle_traceback(self, t, v, tb, is_hook = True):
        """
        将此函数替换sys.excepthook以能够自动网络没有被try...except捕捉的异常,
        使用try except处置的异常需要手动挪用上面的函数handle_one_exception才能够网络
        """
        trace_str = self._get_format_trace_str(t, v, tb)
        self.record_trace(trace_str, is_hook)
        # do something else

    def record_trace(self, trace_str, is_hook):
        # Do somethind
        print 'is_hook: %s' % is_hook
        print trace_str

其用法很简朴:

trace_mgr = TracebackMgr()
sys.excepthook = trace_mgr.handle_traceback
try:
    1 / 0
except Exception as e:
    trace_mgr.handle_one_exception()
    # process trace

1 / '0'

效果用两种方式网络到两个trace信息:

is_hook: False
Traceback (most recent call last):
   File "D:\My Folders\Cnblogs\Alpha Panda\Main.py", line 299, in <module>
    1 / 0
 ZeroDivisionError: integer division or modulo by zero

is_hook: True
Traceback (most recent call last):
   File "D:\My Folders\Cnblogs\Alpha Panda\Main.py", line 304, in <module>
    1 / '0'
 TypeError: unsupported operand type(s) for /: 'int' and 'str'

 可以将尺度的输入和输出重定向,将打印日志和错误信息输入到文件中:

class Dumpfile(object):
    @staticmethod
    def write(str_info):
        with open('./dump_file.txt', 'a+') as fobj:
            fobj.write(str_info)

    def flush(self):
        self.write('')
sys.stdout = sys.stderr = Dumpfile()

trace的网络主要用到两点:若何捕捉异常和两种情形下异常信息的网络,前面都先容过。

5. 总结

python 异常处置:

  1. 使用工具来示意异常错误信息,每种异常均有一种对应的类,BaseException为所有示意异常处置类的基类。
  2. 程序执行历程中抛出的异常会匹配该工具对应的异常类和其所有的基类。
  3. 可以从内置类型的异常类派生出自界说的异常类。
  4. 被捕捉的异常可以再次被抛出。
  5. 可以的话只管使用内置的替换方案,如if getattr(obj, attr_name, None),或者with结构等。
  6. sys.exc_info()保留当前栈帧或者之前的栈帧中获取被try, except捕捉的异常信息。
  7. 未处置的异常导致程序终止前会被sys.excpethook处置,可以自界说界说sys.excpethook。

异常的陷阱:

准确的异常处置能让代码有更好的鲁棒性,然则错误的使用异常会矫枉过正。

捕捉异常却忽略掉或者错误的处置是不可取的。滥用异常处置不仅达不到提高系统稳定性的效果,还会隐藏掉引起错误的诱因,导致排查问题的难度增添。

因此好比何捕捉异常更主要的是,异常发生时应当若何处置。

更多Python相关信息见Python 专题页面 https://www.Linuxidc.com/topicnews.aspx?tid=17

【版权声明】

温馨提示:文章内容系作者个人观点,不代表seo教程网对观点赞同或支持。

版权声明:本文为转载文章,来源于金邦科技 ,版权归原作者所有,欢迎分享本文,转载请保留出处!

网址:www.15544.cn


本文链接:http://15544.cn/Linuxbiancheng/65399.html

最新留言