在设计类时,抽象方法可能非常有用。据我所知,Python没有一种机制来强制继承类来实现抽象方法。在我的代码中(参见下面的示例)我在基类中输入一个失败的断言,如果没有实现则会导致运行时错误。这是unpythonic吗?
class Dog(Animal):
def speak(self):
return "bark"
class Animal():
def speak(self):
assert(False) #abstract
答案 0 :(得分:12)
Python实际上确实具有抽象方法的抽象类:
>>> import abc
>>>
>>> class IFoo(object):
... __metaclass__ = abc.ABCMeta
...
... @abc.abstractmethod
... def foo(self):
... pass
...
>>> foo = IFoo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Cant instantiate abstract class IFoo with abstract methods foo
>>> class FooDerived(IFoo):
... pass
...
>>> foo = FooDerived()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Cant instantiate abstract class FooDerived with abstract methods foo
>>> class FooImplements(FooDerived):
... def foo(self):
... print "foo'ed"
...
>>> foo = FooImplements()
>>> foo.foo()
foo'ed
>>>
另一方面,“的基本问题是”pythonic“有点难以说。如果你的目的是提供抽象基类,以便稍后检查以确保值继承它,那么不,这不是特别pythonic,即使可以使任意类型抽象基类的子类。另一方面,提供一个基于具体子类中提供的实现实现某些功能的抽象基类是完美的。例如,collections.Sequence
和collections.Mapping
只为类似列表和类似dict执行此操作;子类可以提供__getitem__
,并且可以免费获得__contains__
和其他。
可以肯定的是,除了记录代码的期望外,你不应该使用assert()
;如果断言实际上可能失败,则不应使用断言。优化的python(python -O script.py
)不检查断言。
编辑:更多阐述:
如果要检查值的类型:
def foo(bar):
if not isinstance(bar, AbstractBaz):
raise ValueError, ("bar must be an instance of AbstractBaz, "
"got %s" % type(bar))
如果由于某种原因您无法使用@abstractmethod
,但仍希望获得此效果,则应引发NotImplementedError
。您可能希望这样做,因为您确实需要该类的实例,其中一些可能不需要实现可选功能。您仍然应该考虑通过super()
调用该函数的可能性。对于第一个近似值,可能看起来像这样。
class Foo(object):
def bar(self, baz):
if self.bar.im_func == Foo.bar.im_func:
raise NotImplementedError, "Subclasses must implement bar"
答案 1 :(得分:5)
ABCs是C ++的工件,与鸭子打字相反。如果动物类没有定义speak
,它将完全按照您的意图行事。
>>> class Animal(object):
... pass
...
>>> class Dog(Animal):
... def speak(self):
... print "bark"
...
>>> animal = Animal()
>>> dog = Dog()
>>> animal.speak()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Animal' object has no attribute 'speak'
>>> dog.speak()
bark
C ++和相关语言迫使您创建ABC,因为ABC实际上是一个接口描述。 Python避免了编译器强制执行的接口声明,因为它们试图在代码中记录一个通过语言外手段更好地执行的合同。
答案 2 :(得分:2)
好吧,如果你没有在你的基类中包含方法speak
并且不知何故碰巧使用它,那么代码无论如何都会失败。问题是,它是多么可能以及如何告诉用户(NotImplementedError
可能比断言更合适。
答案 3 :(得分:0)
一般来说,使用python并不总是可以直接对代码实施限制,至少从面向对象的角度来看(考虑抽象类,私有方法......)。 要强制实现子类来实现方法,您可能需要执行以下操作:
class Animal():
def speak(self):
raise NotImplementedError #abstract
class Dog(Animal):
def speak(self):
return "bark"
class MuteAnimal(Animal):
pass
这并不意味着该方法将由子类实现,但是当没有实现speak方法时,它只会引发错误。