何时使用'raise NotImplementedError'?

时间:2017-06-01 19:48:33

标签: python oop

是为了记住自己和你的团队正确实施课程吗? 我没有完全使用这样的抽象类:

class RectangularRoom(object):
    def __init__(self, width, height):
        raise NotImplementedError

    def cleanTileAtPosition(self, pos):
        raise NotImplementedError

    def isTileCleaned(self, m, n):
        raise NotImplementedError

5 个答案:

答案 0 :(得分:38)

正如文档所述[docs]

  

在用户定义的基类中,抽象方法在需要派生类覆盖方法时,或者在开发类时指示仍需要添加实际实现时,应引发此异常。

请注意,尽管主要陈述的用例此错误是应该在继承类上实现的抽象方法的指示,但无论如何您都可以使用它,就像指示TODO标记一样

答案 1 :(得分:16)

作为Uriel says,它适用于抽象类中应该在子类中实现的方法,但也可用于指示TODO。

第一个用例有另一种选择:Abstract Base Classes。那些有助于创建抽象类。

这是一个Python 3示例:

class C(abc.ABC):
    @abstractmethod
    def my_abstract_method(self, ...):
    ...

实例化C时,您会收到错误,因为my_abstract_method是抽象的。您需要在子类中实现它。

TypeError: Can't instantiate abstract class C with abstract methods my_abstract_method

子类C并实施my_abstract_method

class D(C):
    def my_abstract_method(self, ...):
    ...

现在您可以实例化D

C.my_abstract_method不一定是空的。可以使用Dsuper()调用它。

优于NotImplementedError的优势在于,您在实例化时获得显式Exception,而不是在方法调用时获得。

答案 2 :(得分:12)

考虑是否是:

class RectangularRoom(object):
    def __init__(self, width, height):
        pass

    def cleanTileAtPosition(self, pos):
        pass

    def isTileCleaned(self, m, n):
        pass

你继承并忘记告诉它如何isTileCleaned(),或者更可能的是,它错误地为isTileCLeaned()。然后在您的代码中,当您调用它时,您将获得None

  • 你会得到你想要的被覆盖的功能吗?绝对没有。
  • None有效输出吗?谁知道。
  • 这是预期的行为吗?几乎肯定不是。
  • 你会收到错误吗?这取决于。

raise NotImplmentedError 强制你实现它,因为当你尝试运行它时抛出异常。这消除了很多无声错误。它类似于bare except is almost never a good idea的原因:因为人们犯了错误,这确保他们不会被淹没在地毯下。

注意:正如其他答案所提到的那样,使用抽象基类更好,因为错误是前载的,程序不会运行直到你实现它们(使用NotImplementedError,它只会引发异常如果真的被称为。)

答案 3 :(得分:12)

还可以在装饰了raise NotImplementedError()的基类方法的子方法中执行@abstractmethod


想象一下为一系列测量模块(物理设备)编写控制脚本。 每个模块的功能都是狭义定义的,仅实现一个专用功能: 一个可能是继电器阵列,另一个可能是多通道DAC或ADC,另一个可能是电流表等。

许多正在使用的低级命令将在模块之间共享,例如读取 他们的ID号或向他们发送命令。让我们看看目前的情况:

基类

from abc import ABC, abstractmethod  #< we'll make use of these later

class Generic(ABC):
    ''' Base class for all measurement modules. '''

    # Shared functions
    def __init__(self):
        # do what you must...

    def _read_ID(self):
        # same for all the modules

    def _send_command(self, value):
        # same for all the modules

共享动词

然后我们意识到许多特定于模块的命令动词,因此, 它们的接口逻辑也被共享。这是3个不同动词的意思 考虑到许多目标模块将是不言自明的。

  • get(channel)

  • 继电器:获取channel上的继电器的开/关状态

  • DAC::获取channel上的输出电压

  • ADC:获取channel

    上的输入电压
  • enable(channel)

  • 继电器::启用channel上的继电器

  • DAC::启用channel

    上的 output 通道的使用
  • ADC::启用channel

    上的 input 通道的使用
  • set(channel)

  • 继电器:打开/关闭channel继电器

  • DAC:设置channel

    上的输出电压
  • ADC:嗯……没有什么逻辑


共享动词成为强制动词

我认为上述动词在各个模块之间共享是有充分理由的 正如我们所看到的,它们对每个人的意义都是显而易见的。我会继续写我的 基类Generic如下:

class Generic(ABC):  # ...continued
    
    @abstractmethod
    def get(self, channel):
        pass

    @abstractmethod
    def enable(self, channel):
        pass

    @abstractmethod
    def set(self, channel):
        pass

子类

我们现在知道我们的子类都必须定义这些方法。让我们看看它是什么 看起来像是ADC模块:

class ADC(Generic):

    def __init__(self):
        super().__init__()  #< applies to all modules
        # more init code specific to the ADC module
    
    def get(self, channel):
        # returns the input voltage measured on the given 'channel'

    def enable(self, channel):
        # enables accessing the given 'channel'

您现在可能想知道:

但这不适用于 ADC 模块,因为 set 在那里没有意义,因为我们上面已经看到了这一点!

您是对的:不实施set是不可行的选择,因为Python会在下面触发错误 当您尝试实例化ADC对象时。

TypeError: Can't instantiate abstract class 'ADC' with abstract methods 'set'

因此您必须实施一些操作,因为我们将set设为了强制动词(又称“ @abstractmethod”), 这是由其他两个模块共享的,但是同时,您也不能实现任何其他 set对这个特定模块没有意义。

抢救时出现NotImplementedError

通过像这样完成ADC课:

class ADC(Generic): # ...continued

    def set(self, channel):
        raise NotImplementedError("Can't use 'set' on an ADC!")

您一次要做三件事:

  1. 您要保护用户免于错误地发出以下命令(“设置”): (不是应该!)对此模块不实施。
  2. 您正在明确地告诉他们问题出在哪里(请参阅TemporalWolf的链接,以了解有关 为什么这很重要的“例外”
  3. 您正在保护强制执行动词的所有其他模块的实现 确实有道理。即您确保这些动词要做有意义的模块将 实现这些方法,他们将完全使用这些动词而不是某些 其他临时名称。

答案 4 :(得分:0)

您可能希望使用@property装饰器

>>> class Foo():
...     @property
...     def todo(self):
...             raise NotImplementedError("To be implemented")
... 
>>> f = Foo()
>>> f.todo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in todo
NotImplementedError: To be implemented