2011-07-04 25 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