【问题标题】:Why am I able to instantiate my Abstract Base Class in Python?为什么我能够在 Python 中实例化我的抽象基类?
【发布时间】:2014-10-18 17:23:19
【问题描述】:

据我了解,我可以使用 Python 中的 abc 模块来创建无法实例化的抽象类(以及其他不错的属性)。我尝试使用它来创建Exception 类的层次结构来表示我的应用程序的各种退出代码,但我仍然能够实例化我的基类,即使我不希望这种情况发生。下面是一些演示问题的代码:

#!/usr/bin/env python3

import abc

class ExitCodeException(Exception):
    __metaclass__ = abc.ABCMeta

    def __init__(self, message):
        super().__init__()
        self._message = message

    @abc.abstractmethod
    def getExitCode(self):
        """Return the exit code for this exception"""
        return

class FatalException(ExitCodeException):
    def getExitCode(self):
        return 1

raise ExitCodeException("Oh no!")

我希望我的程序退出并出现一个异常,指出无法实例化 ExitCodeException,但相反,如果 ExitCodeException 不是抽象的,我会得到我期望的标准堆栈跟踪:

Traceback (most recent call last)
  File "./email2pdf_classexception", line 21, in <module>
    raise ExitCodeException("Oh no!")
__main__.ExitCodeException

我该如何解决这个问题?

【问题讨论】:

  • 在 Python3 中你应该使用关键字语法:class ExitCodeException(Exception, metaclass = abc.ABCMeta):。但是,这不足以解决您的问题。 Exception 基类使实例化成为可能——没有它,一切都按预期工作。但我想它不能解决你的问题,因为你想从这个基类派生出可抛出的异常。
  • 首先,Python3 中定义元类的语法发生了变化。它必须作为关键字参数(称为metaclass)传递给类定义(参考:docs.python.org/3.3/reference/…)。其次,我一个 ABC 不能有一个不是 ABC 的基类。因此,如果您想让ExitCodeException 成为 ABC,则不能将 Exception(不是 ABC)作为 ExitCodeException 的基类。所以,我认为没有一种直接的方法可以让你的代码正常工作。
  • 为什么不能实例化基类很重要?如果您不想实例化它,就不要实例化它。这感觉就像尝试用 Python 编写 Java。
  • @BartoszKP,Debanshu Kundu:你们都在评论中给出了相同的答案。把它放在答案中! :)
  • @AndrewFerrier:Python 不是一门严格的语言。有很多你不应该做的事情,Python 不会阻止你去做。谁在尝试实例化您的基本异常?他们为什么这样做?这感觉就像在寻找一个非问题的解决方案。

标签: python exception abstract-class python-3.4 abc


【解决方案1】:

正如上面 @BartoszKP 和 @Debanshu Kundu 在 cmets 中所讨论的,具体的超类 Exception 似乎是导致问题的原因。因此,我想出了一个稍微不同的模式,它似乎可以工作(据我了解,这是 Python 2 中的旧式模式,但似乎仍然有效):

#!/usr/bin/env python3

class ExitCodeException(Exception):
    def __new__(cls, *args, **kwargs):
        if cls is ExitCodeException:
            raise NotImplementedError("Base class may not be instantiated")
        return Exception.__new__(cls, *args, **kwargs)

    def __init__(self, message):
        super().__init__()
        self._message = message

    def getExitCode(self):
        """Return the exit code for this exception"""
        return

class FatalException(ExitCodeException):
    def getExitCode(self):
        return 1

raise FatalException("Oh no!")

这按预期工作;如果我将代码更改为直接实例化ExitCodeException,它会失败。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-03-08
    • 2015-03-19
    • 1970-01-01
    • 2019-09-07
    • 2011-07-05
    • 2022-07-22
    • 1970-01-01
    相关资源
    最近更新 更多