2011-07-04 23 views
6

设计类时抽象方法可能非常有用。据我所知,Python没有强制继承类来实现抽象方法的机制。在我的代码中(请参阅下面的示例),如果未实现,则在基类中输入失败的断言以导致运行时错误。这是unpythonic?正在执行unpythonic的抽象方法实现吗?

class Dog(Animal): 
    def speak(self): 
    return "bark" 

class Animal(): 
    def speak(self): 
    assert(False) #abstract 

回答

2

好吧,如果你不包括方法speak在你的基类,不知何故发生在使用它,代码反正失败。问题是,它有多大可能以及如何告诉用户(一个NotImplementedError在这里可能比断言更适合)。

11

Python的实际确实有抽象类与abstact方法:

>>> 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 
>>> 

在另一方面,根本问题“是这样的Python的”是有点难说。如果你的意图是提供抽象基类,以便稍后检查以确保值继承它,那么不,那不是特别的pythonic,即使可以制作任意类型的基类的抽象子类。另一方面,提供基于具体子类中提供的实现来实现某些功能的抽象基类是完全正确的。例如,collections.Sequencecollections.Mapping对于像列表和类似词典这样的列表可以做到这一点;子类可以提供__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" 
0

与Python在总体上可并不总是能够直接执行的代码的限制,在从视面向对象点至少(思考抽象类,私有方法,...)。 要强制子类来实现你可能想这样做的方法:

class Animal(): 
    def speak(self): 
    raise NotImplementedError #abstract 

class Dog(Animal): 
    def speak(self): 
    return "bark" 

class MuteAnimal(Animal): 
    pass 

这并不意味着该方法将在子类实现,但它只会引发一个错误时,讲方法没有实施。

5

ABCs是C++的人工产物,与duck-typing相反。如果Animal类没有定义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试图在代码中记录一种通过超语言手段更好地执行的契约,从而避免了编译器强制的接口声明。

+3

“Python避免了编译器强制的接口声明,因为它们试图在代码中记录一种通过超语言学方式更好地执行的合同。” WTF?请澄清此声明,这可能会使其他人更有帮助。 – hiwaylon

+7

此外,如果您要求图书馆的用户在课堂上实施混合教学的方法,那么ABCs和/或抽象方法在鸭子打字世界中是完全有效的。谁在乎,如果它是适当的软件工程,它是“不扩张的”,还是反对特定社区的灌输信仰。看看TokenMacGuy的回答,忽略这个人。 – hiwaylon