Python实例浅谈之二自定义异常

一、问题

      Python中标准异常集包含的内容已经相当广泛,但有时开发中还须创建自己的异常,比如在特定的标准异常和模块异常中添加额外的信息。
       本例中两个异常都与IOError有关,IOError是一个用于输入/输出的通用异常,可能在无效的文件访问或其他形式的通信中触发。

二、解决

1、创建自定义异常代码

  1. #!/usr/bin/env python  
  2. '''  
  3. $Id$  
  4.   
  5. myexc.py -- "my exceptions" demo which highlights user-created  
  6.     exceptions.  NOTE:  this example does not currently work with  
  7.     JPython as neither the errno nor tempfile modules have been  
  8.     implemented, and also, the socket module is incomplete.  
  9. '''  
  10.   
  11. # import all our needed modules  
  12. import os, socket, errno, types, tempfile  
  13.   
  14. # create our a new NetworkError exception, derived from IOError  
  15. class NetworkError(IOError):  
  16.     pass  
  17.   
  18. # create our a new FileError exception, derived from IOError  
  19. class FileError(IOError):  
  20.     pass  
  21.   
  22. # updArgs --> tuple  
  23. def updArgs(args, newarg=None):  
  24.     '''updArgs(args, newarg=None) -- if instance, grab each exception  
  25.         instance argument and place them in a list; otherwise, just  
  26.         convert given args sequence to a list for mutability; add  
  27.         newarg if necessary; then convert the whole thing to a tuple.'''  
  28.   
  29.     if isinstance(args, IOError):  
  30.         myargs = []  
  31.     myargs.extend([arg for arg in args])  
  32.     else:  
  33.         myargs = list(args)  
  34.   
  35.     if newarg:  
  36.         myargs.append(newarg)  
  37.   
  38.     return tuple(myargs)  
  39.   
  40.   
  41. # fileArgs --> tuple  
  42. def fileArgs(fn, mode, args):  
  43.     '''fileArgs(fn, mode, args) -- similar to updArgs() except made  
  44.         specifically for files; creates small permission string and  
  45.         formats error to be similar to other IOError exceptions.'''  
  46.   
  47.     if args[0] == errno.EACCES and \  
  48.             'access' in dir(os):  
  49.         perms = ''  
  50.         permd = { 'r': os.R_OK, 'w': os.W_OK, \  
  51.                     'x': os.X_OK }  
  52.         pkeys = permd.keys()  
  53.         pkeys.sort()  
  54.         pkeys.reverse()  
  55.   
  56.         for eachPerm in 'rwx':  
  57.             if os.access(fn, permd[eachPerm]):  
  58.                 perms = perms + eachPerm  
  59.             else:  
  60.                 perms = perms + '-'  
  61.   
  62.         if isinstance(args, IOError):  
  63.             myargs = []  
  64.         myargs.extend([arg for arg in args])  
  65.         else:  
  66.             myargs = list(args)  
  67.   
  68.         myargs[1] = "'%s' %s (perms: '%s')" % \  
  69.                     (mode, myargs[1], perms)  
  70.   
  71.         myargs.append(args.filename)  
  72.   
  73.     else:  
  74.         myargs = args  
  75.   
  76.     return tuple(myargs)  
  77.   
  78. # myconnect() --> None (raises exception on error)  
  79. def myconnect(sock, host, port):  
  80.     '''myconnect(sock, host, port) -- attempt to make a network connection  
  81.     with the given socket and host-port pair; raises our new NetworkError  
  82.     exception and collates error number and reason.'''  
  83.   
  84.     try:  
  85.         sock.connect((host, port))  
  86.   
  87.     except socket.error, args:  
  88.         myargs = updArgs(args)        # convert inst to tuple  
  89.         if len(myargs) == 1:        # no #s on some errors  
  90.             myargs = (errno.ENXIO, myargs[0])  
  91.   
  92.         raise NetworkError, \  
  93.             updArgs(myargs, host + ':' + str(port))  
  94.   
  95.   
  96. # myopen() --> file object  
  97. def myopen(fn, mode='r'):  
  98.     '''myopen(fn, mode) -- wrapper around the open() built-in function  
  99.     such that we raise our new FileError exception on an error situation  
  100.     and collate a set of FileError exception arguments to pass to the user'''  
  101.   
  102.     try:  
  103.         fo = open(fn, mode)  
  104.   
  105.     except IOError, args:  
  106.         raise FileError, fileArgs(fn, mode, args)  
  107.   
  108.     return fo  
  109.   
  110.   
  111. # testfile() --> None  
  112. def testfile():  
  113.     '''testfile() -- runs the file tester, setting a variety of test files  
  114.     which should generate FileError exceptions'''  
  115.   
  116.     fn = tempfile.mktemp()      #make temp file and path  
  117.     f = open(fn, 'w')  
  118.     f.close()  
  119.   
  120.     for eachTest in ((0, 'r'), (0100, 'r'), (0400, 'w'), (0500, 'w')):  
  121.         try:  
  122.             os.chmod(fn, eachTest[0])  
  123.             f = myopen(fn, eachTest[1])  
  124.         except FileError, args:  
  125.             print "%s: %s" % \  
  126.                     (args.__class__.__name__, args)  
  127.         else:  
  128.             print fn, "opened ok... perms ignored"  
  129.             f.close()  
  130.   
  131.     os.chmod(fn, 0777)  
  132.     os.unlink(fn)  
  133.   
  134.   
  135. # testnet() --> None  
  136. def testnet():  
  137.     '''testfile() -- runs the network tester, making various connections  
  138.     which should generate NetworkError exceptions'''  
  139.     s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
  140.   
  141.     for eachHost in ('127.0.0.1', 'www'):  
  142.         try:  
  143.             myconnect(s, eachHost, 80)  
  144.         except NetworkError, args:  
  145.             print "%s: %s" % (args.__class__.__name__, args)  
  146.         else:  
  147.             print "network connection successful to", `eachHost`  
  148.             s.close()  
  149.   
  150.   
  151. # run tests if invoked as a script  
  152. if __name__ == '__main__':  
  153.     testfile()  
  154.     testnet()  
         程序自定义了两个新的异常类FileError与NetworkError,基类都是IOError,也重新实现了两个诊断版的函数open()[myopen()]和socket.connect([myconnect()],同时包含了一个在直接运行文件时执行的测试函数[test()]。

2、运行结果图

(centos6.5下运行结果:)

Python实例浅谈之二自定义异常
        myconnect()仅仅是简单的对套接字的函数conect()进行包装,当网络连接失败时提供一个IOError类型的异常,和一般的socket.error不一样,还提供给程序员主机名和端口号。当失败发生时,错误号和错误字符很有帮助,但是如果结合更精确的主机-端口会更有帮助,因为这一对可能是由某个数据库或名称服务动态生成或重新获得。这些值由connect()加入。另一种情形是无法找到主机,socket.error异常没有直接提供的错误号;为了遵循IOError协议,提供了一个错误号-错误字符串对;查找最接近的错误号,选用的是ENXIO。
        myopen()封装了已经存在的一些代码,这里用的是open()函数,仅仅捕捉IOError异常。所有的其他都忽略并传给下一层(因为没有与它们相关的处理器)。一旦捕捉到IOError就引发自定义的异常并通过 fileArgs()返回值来定制参数。

          上述分别是linux下使用不同的用户运行的结果,root用户拥有操作文件的所有权限。

三、总结

(1)可以总结一个通用的异常模块,包括常见的异常信息提示,这样在项目开发中可以直接导入,重复利用。
(2)raise传递异常,对定位错误信息有很大的帮助,并且可以加入自己喜欢的样式便于调试。

(3)若有更好的设计或思路,请留言,在此先感谢!


From: http://blog.csdn.net/taiyang1987912/article/details/44155139