2011-11-18 56 views
28

我有許多類都共享相同的方法,只有不同的實現。在Java中,讓每個類都實現一個接口或擴展一個抽象類是有意義的。 Python是否有類似的東西,或者我應該採取另一種方法?Python中的Java摘要/接口設計

+0

見http://stackoverflow.com/questions/372042/difference抽象類與接口在python中注意到Zope接口模塊的鏈接是一個類java接口的實現。 –

+0

有沒有直接的答案,這是否有接口或不? –

回答

51

Python中的接口背後有一些故事。多年來一直處於搖擺狀態的原始態度是,您不需要它們:Python在EAFP上工作(比請求更容易要求原諒)原則。也就是說,不是指定你接受一個,我不知道,ICloseable對象,當你需要的時候你只需要嘗試close對象,如果它引發異常,那麼它會引發異常。

因此,在這種心態下,你可以單獨編寫你的類,並按照你的意願使用它們。如果其中一個不符合要求,您的計劃將引發異常;相反,如果你使用正確的方法編寫另一個類,那麼它就可以工作,而無需指定它實現了特定的接口。

這工作得很好,但接口有明確的用例,特別是對於較大的軟件項目。 Python中的最終決定是提供abc模塊,它允許你編寫抽象基類即你不能實例化的類,除非你覆蓋所有的方法。這是你的決定,你是否認爲使用它們是值得的。

PEP introducing ABCs解釋遠好於我可以:

在面向對象編程領域,對於 使用模式與對象相互作用可分成兩個基本類別, 其是「調用'和'檢查'。

調用是指通過調用其方法來與對象進行交互。 通常這與多態性相結合,因此調用給定的方法可能運行不同的代碼,具體取決於對象的類型。

檢驗裝置外部代碼檢查類型或對象, 的性質,並就如何基於該 信息來治療對象決定的能力(的 對象的方法外)。

兩種使用模式服務於相同的一般目的,這是爲了能夠 支撐在一個 均勻的方式多樣化和潛在的新的對象的處理,但在同一時間,允許的處理的決定是 定製用於每個不同對象的類型。

在傳統的OOP理論中,調用是首選的使用模式,並且積極勸阻檢查,被視爲以前程序編程風格的遺留物。但是,在實踐中,這種觀點太過於教條化和僵化,並導致了一種設計的剛性,這與Python等語言的動態性質非常不一致。

特別是,通常需要以對象類的創建者沒有預料到的方式處理對象。它不是 總是最好的解決方案,以建立每個對象方法,以滿足該對象的每個可能用戶的需求。此外, 還有許多強大的調度哲學,它們直接與 進行對比,典型的OOP要求是將行爲嚴格地封裝在一個對象內,例如規則或模式匹配 驅動的邏輯。另一方面,對經典OOP 理論家的檢查的批評之一是缺乏形式主義和被檢查的 的特殊性質。在諸如Python之類的語言中,對象的幾乎任何方面都可以被反射並且可以通過外部代碼直接訪問,有許多不同的方式來測試對象是否符合特定的協議或不符合 。例如,如果詢問「是否這個對象是可變序列容器?」,可以查找'list'的基類 ,或者可以查找名爲'getitem'的方法。但注意 ,雖然這些測試可能看起來很明顯,他們都不是 正確,因爲一個生成的假陰性,其他假的 積極。

一般認可的補救措施是標準化測試,並將它們分組爲正式安排。這很容易通過 通過繼承機制或一些其他手段與每個類關聯一組標準可測試屬性 來完成。每個測試 帶有一組承諾:它包含關於該類的一般行爲的承諾,以及承諾哪些其他類可用的方法。

這個PEP提出了一個組織這些測試的特定策略 ,稱爲抽象基類或ABC。ABCs僅僅是Python類 ,它們被添加到對象的繼承樹中以向外部檢查器發送該對象的某些 特徵。測試使用isinstance()使用 完成,並且特定ABC的存在意味着測試 已通過。

此外,ABC定義了一組最小的方法,用於確定類型的特徵行爲。基於它們的ABC類型區分對象的代碼可以相信這些方法總是存在 。每種方法都伴隨着 廣義抽象語義定義,該定義在ABC的 文檔中進行了描述。這些標準語義定義不是 強制執行,但強烈建議。

像Python中的所有其他的事情,這些承諾是在 君子協定,性質,在這種情況下,意味着當 語言並執行一些在ABC作出的承諾的,它是由 到確保其餘的 保持不變。

+0

來吧,這是很長的,有沒有接口? –

+0

是的,他們被稱爲抽象基類 – Matt

3

也許你可以使用這樣的東西。這將作爲一個抽象類。因此,每一個子類是被迫實施FUNC1()

class Abstract: 

    def func1(self): 
     raise NotImplementedError("The method not implemented") 
+0

這已經在stdlib中作爲'abc'(http://docs.python.org/library/abc.html)。 – katrielalex

+1

好吧,'abc'比前面的例子好得多,因爲'abc'會在創建類時產生錯誤,而前面的例子只會在調用方法時引發。 – madjar

4

我沒那麼熟悉Python,但它不我會大膽地猜測。

接口存在於Java中的原因是它們指定合同。舉例來說,實施java.util.List的東西保證有一個add()方法來符合界面上定義的一般行爲。您可以在不知道其特定類的情況下放入List的任何(理性)實現,調用接口上定義的一系列方法並獲得相同的一般行爲。

此外,開發人員和編譯器都可以知道這種方法存在並且可以在所討論的對象上調用,即使他們不知道它的確切類。這是一種多態的形式,需要靜態類型來允許不同的實現類,但仍然知道它們都是合法的。

這在Python中沒有意義,因爲它不是靜態類型的。你不需要聲明對象的類,也不需要說服編譯器你調用的方法肯定存在。鴨子式的世界中的「接口」與調用該方法一樣簡單,並且相信該對象可以適當地處理該消息。

注 - 歡迎閱讀更多知識淵博的Pythonistas編輯。

+0

或者因爲Java中沒有多繼承。 –

0

我寫了一個library在3.5+允許在Python中編寫接口。

要點是在inspect的幫助下編寫一個類裝飾器。

import inspect 


def implements(interface_cls): 
    def _decorator(cls): 
     verify_methods(interface_cls, cls) 
     verify_properties(interface_cls, cls) 
     verify_attributes(interface_cls, cls) 
     return cls 

    return _decorator 


def verify_methods(interface_cls, cls): 
    methods_predicate = lambda m: inspect.isfunction(m) or inspect.ismethod(m) 
    for name, method in inspect.getmembers(interface_cls, methods_predicate): 
     signature = inspect.signature(method) 
     cls_method = getattr(cls, name, None) 
     cls_signature = inspect.signature(cls_method) if cls_method else None 
     if cls_signature != signature: 
      raise NotImplementedError(
       "'{}' must implement method '{}({})' defined in interface '{}'" 
       .format(cls.__name__, name, signature, interface_cls.__name__) 
      ) 


def verify_properties(interface_cls, cls): 
    prop_attrs = dict(fget='getter', fset='setter', fdel='deleter') 
    for name, prop in inspect.getmembers(interface_cls, inspect.isdatadescriptor): 
     cls_prop = getattr(cls, name, None) 
     for attr in prop_attrs: 
      # instanceof doesn't work for class function comparison 
      if type(getattr(prop, attr, None)) != type(getattr(cls_prop, attr, None)): 
       raise NotImplementedError(
        "'{}' must implement a {} for property '{}' defined in interface '{}'" # flake8: noqa 
        .format(cls.__name__, prop_attrs[attr], name, interface_cls.__name__) 
       ) 


def verify_attributes(interface_cls, cls): 
    interface_attributes = get_attributes(interface_cls) 
    cls_attributes = get_attributes(cls) 
    for missing_attr in (interface_attributes - cls_attributes): 
     raise NotImplementedError(
      "'{}' must have class attribute '{}' defined in interface '{}'" 
      .format(cls.__name__, missing_attr, interface_cls.__name__) 
     ) 


def get_attributes(cls): 
    boring = dir(type('dummy', (object,), {})) 
    return set(item[0] for item in inspect.getmembers(cls) 
       if item[0] not in boring and not callable(item[1])) 

然後,您可以編寫類是這樣的:

class Quackable: 
    def quack(self) -> bool: 
     pass 


@implements(Quackable) 
class MallardDuck:  
    def quack(self) -> bool: 
     pass 

下面會給你雖然一個錯誤:

@implements(Quackable) 
class RubberDuck:  
    def quack(self) -> str: 
     pass 

NotImplementedError: 'RubberdDuck' must implement method 'quack((self) -> bool)' defined in interface 'Quackable'