Can one declare an abstract exception in Python?

2019-06-20 05:17发布

问题:

I would like to declare a hierarchy of user-defined exceptions in Python. However, I would like my top-level user-defined class (TransactionException) to be abstract. That is, I intend TransactionException to specify methods that its subclasses are required to define. However, TransactionException should never be instantiated or raised.

I have the following code:

from abc import ABCMeta, abstractmethod

class TransactionException(Exception):
    __metaclass__ = ABCMeta

    @abstractmethod
    def displayErrorMessage(self):
        pass

However, the above code allows me to instantiate TransactionException...

a = TransactionException()

In this case a is meaningless, and should instead draw an exception. The following code removes the fact that TransactionException is a subclass of Exception...

from abc import ABCMeta, abstractmethod

class TransactionException():
    __metaclass__ = ABCMeta

    @abstractmethod
    def displayErrorMessage(self):
        pass

This code properly prohibits instantiation but now I cannot raise a subclass of TransactionException because it's not an Exception any longer.

Can one define an abstract exception in Python? If so, how? If not, why not?

NOTE: I'm using Python 2.7, but will happily accept an answer for Python 2.x or Python 3.x.

回答1:

There's a great answer on this topic by Alex Martelli here. In essence, it comes down how the object initializers (__init__) of the various base classes (object, list, and, I presume, Exception) behave when abstract methods are present.

When an abstract class inherits from object (which is the default, if no other base class is given), it's __init__ method is set to that of object's, which performs the heavy-lifting in checking if all abstract methods have been implemented.

If the abstract class inherits from a different base class, it will get that class' __init__ method. Other classes, such as list and Exception, it seems, do not check for abstract method implementation, which is why instantiating them is allowed.

The other answer provides a suggested workaround for this. Of course, another option that you have is simply to accept that the abstract class will be instantiable, and try to discourage it.



回答2:

class TransactionException(Exception):

    def __init__(self, *args, **kwargs):
        raise NotImplementedError('you should not be raising this')


class EverythingLostException(TransactionException):

    def __init__(self, msg):
        super(TransactionException, self).__init__(msg)


try:
    raise EverythingLostException('we are doomed!')
except TransactionException:
    print 'check'

try:
    raise TransactionException('we are doomed!')
except TransactionException:
    print 'oops'