-->

传承最佳实践:* ARGS,** kwargs或明确指定参数(Inheritance best pr

2019-08-16 16:32发布

我经常发现自己重写父类的方法,并不能决定我是否应该明确列出给定参数或只使用一条毯子*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中的禅)
  • 更容易掌握
  • 功能参数很容易地访问

毯变种的预期效益

  • 更干
  • 父类是容易互换
  • 在父类的方法的缺省值的变化而不触及其它代码被传播

Answer 1:

里氏替换原则

一般来说,你不希望你的方法签名在派生类型而有所不同。 如果你想交换使用派生类型的可能会出现问题。 这通常被称为里氏替换原则 。

显式签名的好处

同时,我不认为这是正确的为您的所有方法有一个签名*args**kwargs 。 明确的签名:

  • 帮助记录通过良好的参数名的方法
  • 帮助通过指定哪些是必需的,哪些参数传递给文件鉴别方法有默认值
  • 提供隐性验证(缺少必需的ARGS抛出明显的例外)

可变长度参数和耦合

不要误认为是良好的耦合做法可变长度参数。 应该有一个父类和派生类,否则他们就不会涉及到相互之间有一定量的凝聚力。 这是正常的,导致反映团结度耦合相关的代码。

地方使用可变长度参数

的可变长度参数使用不应该是你的第一选择。 当你有一个很好的理由应该使用这样的:

  • 定义一个函数包装(即装饰)。
  • 定义的参数多态函数。
  • 当你能接受的参数真的是完全变量(例如广义DB连接功能)。 DB连接功能通常需要的连接字符串在许多不同的形式,无论是在单个arg格式,和在多arg格式。 也有不同的套不同数据库的选项。
  • ...

你做错了什么?

如果你发现自己经常创造它需要很多参数或导出方法不同签名的方法,你可以在你如何组织代码的一个更大的问题。



Answer 2:

我的选择是:

class Child(Parent):

    def save(self, commit=True, **kwargs):
        super(Child, self).save(commit, **kwargs)
        # more logic

它避免了访问来自提交论证*args**kwargs ,它让事情变得安全的,如果签字Parent:save的更改(例如添加新的默认参数)。

更新 :在这种情况下,具有* ARGS可引起麻烦,如果一个新的位置参数添加到父。 我将只保留**kwargs和使用默认值只管理新的论据。 这将避免错误传播。



Answer 3:

如果您确信孩子将保持签名,肯定是明确的做法是可取的,但如果孩子会更改签名我个人更喜欢使用这两种方法:

class Parent(object):
    def do_stuff(self, a, b):
        # some logic

class Child(Parent):
    def do_stuff(self, c, *args, **kwargs):
        super(Child, self).do_stuff(*args, **kwargs)
        # some logic with c

这样一来,在签名的变化是在儿童相当的可读性,而原来的签名是在家长相当的可读性。

在我看来,这也是当你有多重继承,因为调用更好的办法super几次是很恶心的,当你没有指定参数和kwargs。

对于它的价值,这也是不少的Python库和框架的首选方式(Django的,龙卷风,采购,降价,仅举几例)。 尽管我们不应当立足于这样的事情他的选择,我只是暗示,这种做法是相当普遍的。



Answer 4:

不是一个真正的答案,但更多的是侧面说明:如果你真的要确保父类的默认值被传播到子类,你可以这样做:

class Parent(object):

    default_save_commit=True
    def save(self, commit=default_save_commit):
        # ...

class Derived(Parent):

    def save(self, commit=Parent.default_save_commit):
        super(Derived, self).save(commit=commit)

但是我不得不承认,这看起来相当丑陋,我只会用它,如果我觉得我真的需要它。



Answer 5:

我更喜欢明确的参数,因为自动完成让你看到函数的方法签名,同时使函数调用。



Answer 6:

除了其他的答案:

具有可变参数的可能“脱钩”,从孩子的父母,但创建创建对象和家长,我认为这是糟糕之间的耦合,因为现在你创建了一个“长距离”夫妇(更难发现,更难维护,因为你可能在你的应用程序中创建多个对象)

如果你正在寻找去耦,看看在继承组成



文章来源: Inheritance best practice : *args, **kwargs or explicitly specifying parameters