继承最佳实践:* args,** kwargs或显式指定参数
我经常发现自己会覆盖父类的方法,并且永远无法决定是应该显式列出给定的参数还是仅使用通用*args,
**kwargs
结构。一个版本比另一个版本好吗?有最佳做法吗?我缺少什么(缺点)?
class Parent(object):
def save(self, commit=True):
# ...
class Explicit(Parent):
def save(self, commit=True):
super(Explicit, self).save(commit=commit)
# more logic
class Blanket(Parent):
def save(self, *args, **kwargs):
super(Blanket, self).save(*args, **kwargs)
# more logic
显式变体的感知好处
- 更明确(Python的禅宗)
- 更容易掌握
- 功能参数易于访问
毯子变体的感知优势
- 更干
- 父类很容易互换
- 无需更改其他代码即可传播父方法中默认值的更改
-
里斯科夫替代原则
通常,您不希望方法签名在派生类型中有所不同。如果要交换派生类型的使用,可能会导致问题。这通常称为Liskov替代原理。
显式签名的好处
同时,我不认为这是正确的为您的所有方法有一个签名
*args
,**kwargs
。显式签名:- 通过良好的参数名称帮助记录方法
- 通过指定哪些args和哪些具有默认值来帮助记录该方法
- 提供隐式验证(缺少必需的参数会引发明显的异常)
可变长度参数和耦合
不要将变长参数误认为是良好的耦合实践。父类和派生类之间应该有一定的凝聚力,否则它们将不会相互关联。相关代码导致耦合反映内聚程度是正常的。
可变长度参数的使用位置
使用可变长度参数不应该是您的第一选择。当您有充分的理由时应使用它:
- 定义函数包装器(即装饰器)。
- 定义参数多态函数。
- 当您可以接受的参数确实是完全可变的时(例如,通用的数据库连接功能)。DB连接函数通常采用多种不同形式的连接字符串,包括单arg形式和多arg形式。对于不同的数据库,还有不同的选项集。
- …
您做错什么了吗?
如果发现自己经常创建带有许多参数的方法或带有不同签名的派生方法,那么在组织代码的方式上可能会遇到更大的问题。