是为了记住自己和你的团队正确实施课程吗? 我没有完全使用这样的抽象类:
class RectangularRoom(object):
def __init__(self, width, height):
raise NotImplementedError
def cleanTileAtPosition(self, pos):
raise NotImplementedError
def isTileCleaned(self, m, n):
raise NotImplementedError
答案 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
不一定是空的。可以使用D
从super()
调用它。
优于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
ADC::启用channel
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
对这个特定模块没有意义。
通过像这样完成ADC课:
class ADC(Generic): # ...continued
def set(self, channel):
raise NotImplementedError("Can't use 'set' on an ADC!")
您一次要做三件事:
答案 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