2017-08-05 62 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级像其他孩子的,他会得到一个警告,提醒他,但是他可以轻松地一步通过试穿语法。