一、问题

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

二、解决

1、创建自定义异常代码

#!/usr/bin/env python
'''
$Id$

myexc.py -- "my exceptions" demo which highlights user-created
    exceptions.  NOTE:  this example does not currently work with
    JPython as neither the errno nor tempfile modules have been
    implemented, and also, the socket module is incomplete.
'''

# import all our needed modules
import os, socket, errno, types, tempfile

# create our a new NetworkError exception, derived from IOError
class NetworkError(IOError):
    pass

# create our a new FileError exception, derived from IOError
class FileError(IOError):
    pass

# updArgs --> tuple
def updArgs(args, newarg=None):
    '''updArgs(args, newarg=None) -- if instance, grab each exception
        instance argument and place them in a list; otherwise, just
        convert given args sequence to a list for mutability; add
        newarg if necessary; then convert the whole thing to a tuple.'''

    if isinstance(args, IOError):
        myargs = []
	myargs.extend([arg for arg in args])
    else:
        myargs = list(args)

    if newarg:
        myargs.append(newarg)

    return tuple(myargs)


# fileArgs --> tuple
def fileArgs(fn, mode, args):
    '''fileArgs(fn, mode, args) -- similar to updArgs() except made
        specifically for files; creates small permission string and
        formats error to be similar to other IOError exceptions.'''

    if args[0] == errno.EACCES and \
            'access' in dir(os):
        perms = ''
        permd = { 'r': os.R_OK, 'w': os.W_OK, \
                    'x': os.X_OK }
        pkeys = permd.keys()
        pkeys.sort()
        pkeys.reverse()

        for eachPerm in 'rwx':
            if os.access(fn, permd[eachPerm]):
                perms = perms + eachPerm
            else:
                perms = perms + '-'

        if isinstance(args, IOError):
            myargs = []
	    myargs.extend([arg for arg in args])
        else:
            myargs = list(args)

        myargs[1] = "'%s' %s (perms: '%s')" % \
                    (mode, myargs[1], perms)

        myargs.append(args.filename)

    else:
        myargs = args

    return tuple(myargs)

# myconnect() --> None (raises exception on error)
def myconnect(sock, host, port):
    '''myconnect(sock, host, port) -- attempt to make a network connection
    with the given socket and host-port pair; raises our new NetworkError
    exception and collates error number and reason.'''

    try:
        sock.connect((host, port))

    except socket.error, args:
        myargs = updArgs(args)        # convert inst to tuple
        if len(myargs) == 1:        # no #s on some errors
            myargs = (errno.ENXIO, myargs[0])

        raise NetworkError, \
            updArgs(myargs, host + ':' + str(port))


# myopen() --> file object
def myopen(fn, mode='r'):
    '''myopen(fn, mode) -- wrapper around the open() built-in function
    such that we raise our new FileError exception on an error situation
    and collate a set of FileError exception arguments to pass to the user'''

    try:
        fo = open(fn, mode)

    except IOError, args:
        raise FileError, fileArgs(fn, mode, args)

    return fo


# testfile() --> None
def testfile():
    '''testfile() -- runs the file tester, setting a variety of test files
    which should generate FileError exceptions'''

    fn = tempfile.mktemp()      #make temp file and path
    f = open(fn, 'w')
    f.close()

    for eachTest in ((0, 'r'), (0100, 'r'), (0400, 'w'), (0500, 'w')):
        try:
            os.chmod(fn, eachTest[0])
            f = myopen(fn, eachTest[1])
        except FileError, args:
            print "%s: %s" % \
                    (args.__class__.__name__, args)
        else:
            print fn, "opened ok... perms ignored"
            f.close()

    os.chmod(fn, 0777)
    os.unlink(fn)


# testnet() --> None
def testnet():
    '''testfile() -- runs the network tester, making various connections
    which should generate NetworkError exceptions'''
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    for eachHost in ('127.0.0.1', 'www'):
        try:
            myconnect(s, eachHost, 80)
        except NetworkError, args:
            print "%s: %s" % (args.__class__.__name__, args)
        else:
            print "network connection successful to", `eachHost`
            s.close()


# run tests if invoked as a script
if __name__ == '__main__':
    testfile()
    testnet()


         程序自定义了两个新的异常类FileError与NetworkError,基类都是IOError,也重新实现了两个诊断版的函数open()[myopen()]和socket.connect([myconnect()],同时包含了一个在直接运行文件时执行的测试函数[test()]。

2、运行结果图

(centos6.5下运行结果:)


类型提示 python python对类型无效的操作_类型提示 python


        myconnect()仅仅是简单的对套接字的函数conect()进行包装,当网络连接失败时提供一个IOError类型的异常,和一般的socket.error不一样,还提供给程序员主机名和端口号。当失败发生时,错误号和错误字符很有帮助,但是如果结合更精确的主机-端口会更有帮助,因为这一对可能是由某个数据库或名称服务动态生成或重新获得。这些值由connect()加入。另一种情形是无法找到主机,socket.error异常没有直接提供的错误号;为了遵循IOError协议,提供了一个错误号-错误字符串对;查找最接近的错误号,选用的是ENXIO。

        myopen()封装了已经存在的一些代码,这里用的是open()函数,仅仅捕捉IOError异常。所有的其他都忽略并传给下一层(因为没有与它们相关的处理器)。一旦捕捉到IOError就引发自定义的异常并通过 fileArgs()返回值来定制参数。


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


三、总结


(1)可以总结一个通用的异常模块,包括常见的异常信息提示,这样在项目开发中可以直接导入,重复利用。

(2)raise传递异常,对定位错误信息有很大的帮助,并且可以加入自己喜欢的样式便于调试。

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