2017-08-05 150 views
3

我試圖弄清楚什麼是在Python中繼承原則的最佳實踐,當在子中更改方法簽名時存在「壞主意」。繼承:更改子方法的簽名

讓我們假設我們有一些基礎類BaseClient與已經實施的create方法(和一些抽象的)適合適合幾乎所有的「後代」,除了一個:

class BaseClient(object): 

    def __init__(self, connection=None): 
     pass 

    def create(self, entity_id, data=None): 
     pass 

class ClientA(BaseClient): 
    pass 

class ClientB(BaseClient): 
    pass 

中唯一的類ClientC需要的另一種實現方式create方法帶有一點點的另一種方法簽名

class ClientC(BaseClient): 
    .... 
    def create(self, data): 
     pass 

所以現在的問題是如何使這個更「Python化」的方式,考慮到最好蟒蛇的做法?當然,我們可以在父(子)方法中使用*args, **kwargs和其他**kwargs類似的方法,但是恐怕它會使我的代碼不易讀(自我記錄)。

+4

替換原理:通常不是一個好的設計,它有一個不能做基類的子類 - 'x.create(entity_id)'適用於'BaseClient',因此它應該適用於它的子類。 – zch

+0

爲什麼ClientC需要另一個創建實現? – Gribouillis

+3

什麼zch說:一個子類型可以添加新的參數方法簽名,但它不應該刪除它們,因爲它違反[Liskov替代原則](https://en.wikipedia.org/wiki/Liskov_substitution_principle)。 –

回答

0

我不確定是否存在Pythonic這樣做的方式,就像您在問題中所做的那樣。相反,我會說這是關於面向對象而不是Pythonic的問題。

所以我認爲有其他BaseClientcreate實現的其他方法,其他的孩子共享(否則,沒有一點正在ClientCBaseClient孩子)。在你的情況下,看起來像ClientC是不同的,因爲需要不同的簽名create方法。那麼也許是考慮分裂它們的情況?

例如,你可以有根BaseClient實現除create所有共享的方法,然後有兩個「基地」孩子,像這樣:

class EntityBasedClient(BaseClient): 
    def create(self, entity_id, data=None): 
     pass 

class DataBasedClient(BaseClient): 
    def create(self, data): 
     pass 

所以現在你可以繼承不違反任何規則:

class ClientA(EntityBasedClient): 
    pass 

class ClientB(EntityBasedClient): 
    pass 

class ClientC(DataBasedClient): 
    pass 

而且,如果create實施Ø如果這兩個版本非常相似,則可以通過在BaseClient中使用BaseClient執行的更通用的私有方法來避免代碼重複,然後使用簽名_create(self, entity_id=None, data=None),然後使用EntityBasedClientDataBasedClient中的適當參數對其進行調用。

+0

我認爲這很讓人困惑,因爲之前編寫的期望BaseClient的函數現在只能用於EntityBasedClient。這使代碼難以理解。 ClientC中的'create_from_data()'方法似乎更加健壯。正如Martineau所說,需要調用create_from_data()的代碼知道它處理ClientC實例。 – Gribouillis

+0

「...以前編寫的函數,期望BaseClient現在只能使用EntityBasedClient」。爲什麼會是這種情況?正如我所提到的,所有的共享方法(也是共享簽名)都應該在'BaseClient'中實現,因此可以從任何孩子訪問。或者我錯過了什麼? – bagrat

+0

你的意思是,只要他們不調用create(),他們仍然會工作。每個功能都必須仔細審查。 – Gribouillis

0

我想說,只需添加參數回作爲關鍵字與默認值無。然後引發一個錯誤,解釋一些輸入數據丟失。

class ClientC(BaseClient): 
.... 
    def create(self,entity_id=None, data): 
     if entity_id: 
      raise RedudantInformationError("Value for entity_id does nothing") 
     pass 

這樣,每當程序員試圖處理Child C級像其他孩子的,他會得到一個警告,提醒他,但是他可以輕鬆地一步通過試穿語法。