Python 避免在子类中指定所有参数

Python 避免在子类中指定所有参数,python,constructor,arguments,subclass,Python,Constructor,Arguments,Subclass,我有一门课: class A(object): def __init__(self,a,b,c,d,e,f,g,...........,x,y,z) #do some init stuff 我有一个子类需要一个额外的arg(最后一个W) 编写所有这些锅炉板代码似乎很愚蠢,例如将所有参数从B的Ctor传递到A的Ctor的内部调用,因为对A的Ctor的每次更改都必须应用到B代码中的其他两个位置 我猜python有一些习语来处理我不知道的这种情况。你能给我指一下正确的方向吗

我有一门课:

class A(object):
    def __init__(self,a,b,c,d,e,f,g,...........,x,y,z)
        #do some init stuff
我有一个子类需要一个额外的arg(最后一个
W

编写所有这些锅炉板代码似乎很愚蠢,例如将所有参数从
B
的Ctor传递到
A
的Ctor的内部调用,因为对
A
的Ctor的每次更改都必须应用到
B
代码中的其他两个位置

我猜python有一些习语来处理我不知道的这种情况。你能给我指一下正确的方向吗

我最好的预感是,为a创建一种复制Ctor,然后将B的代码更改为

class B(A):
     def __init__(self,instanceOfA,W):
         A.__copy_ctor__(self,instanceOfA)
         self.__W=W

这将满足我的需要,因为我总是在给定父类的实例时创建子类,尽管我不确定是否可能…

您想要这样的东西吗

class A(object):
    def __init__(self, a, b, c, d, e, f, g):
        # do stuff
        print a, d, g

class B(A):
    def __init__(self, *args):
        args = list(args)
        self.__W = args.pop()
        A.__init__(self, *args)

编辑:根据马特的建议,为了解决格尼布尔的担忧,我们采用了立场论证方法;您可以检查以确保指定的附加子类特定参数类似于:

原始答案:
与使用
super()
相同

使用
super()
调用超类的
\uuu init\uuu()
方法,然后继续初始化子类:

class A(object):
  def __init__(self, a, b):
    self.a = a
    self.b = b

class B(A):
  def __init__(self, w, *args):
    super(B,self).__init__(*args)
    self.w = w

考虑到参数可以通过名称或位置传递,我将编写以下代码:

class B(A):
    def __init__(self, *a, **k):
      if 'W' in k:
        w = k.pop('W')
      else:
        w = a.pop()
      A.__init__(self, *a, **k)
      self._W = w

在传递给
\uuuuu init\uuuu
的某些或所有参数具有默认值的情况下,避免在子类中重复
\uuuuuu init\uuu
方法签名可能会很有用

在这些情况下,
\uuuuu init\uuuu
可以将任何额外参数传递给另一个方法,该方法的子类可以重写:

class A(object):
    def __init__(self, a=1, b=2, c=3, d=4, *args, **kwargs):
        self.a = a
        self.b = b
        # …
        self._init_extra(*args, **kwargs)

    def _init_extra(self):
        """
        Subclasses can override this method to support extra
        __init__ arguments.
        """

        pass


class B(A):
    def _init_extra(self, w):
        self.w = w

这适用于非常简化的版本。但是现在B接受任意数量的arg(较少限制)。。。难道没有更地道的说法吗?B只接受任意数量的参数;它调用A,如果A得到错误的号码,它将引发
TypeError
。如果愿意,可以显式检查参数的数量。我可能会在生产代码中这样做。如果您发现错误的参数数量,您自己也会引发TypeError。是的,但第一个错误是一个明显的错误(例如,pylint在运行代码之前会识别的错误),但是您的解决方案调用的错误只有在运行时才能找到p。我认为此解决方案的另一个缺点是,现在B的init docstring必须是“this init接受a的所有参数,加上W作为最后一个参数”这比在init中显式写入所有参数更糟糕。。。好吧,也许你不能同时从两个方面受益:)这个解决方案要求w必须作为第一个arg或按名称传递。这是最后一个问题arg@gnu谢谢你的评论。我编辑了我的回答,以解决这一问题;与Alex几个小时前发布的内容类似:-)我喜欢这个想法,但我认为这个例子不起作用。由于
*a
导致
a
到,
a
没有
pop()
方法<代码>首先需要将一个转换为一个列表。另外,如果所有参数都是按名称传递的,
a
是空的(因此您不能
pop()
它)。
AttributeError:“tuple”对象在
a.pop()
之前没有属性“pop”
如果您有那么多参数,我不会使用位置参数,而是将它们全部/大部分作为KWARG,并使用py3'*'语法
def_uuiniti_u(arg1,arg2,*kwarg1=…,…,kwargx=…,w=…)
强制kwargs(=不依赖顺序)。使用
def\uuuu init\uuuu(self,arg1,arg2,*,**kwargs)
比较短,但我不喜欢它,因为它会使像pycharm这样的IDE中的代码完成变得混乱。除了一点屏幕浪费,写出所有的Kwarg还有什么害处?无论如何,您都必须在docstring中记录它们。。。
class B(A):
    def __init__(self, *a, **k):
      if 'W' in k:
        w = k.pop('W')
      else:
        w = a.pop()
      A.__init__(self, *a, **k)
      self._W = w
class A(object):
    def __init__(self, a=1, b=2, c=3, d=4, *args, **kwargs):
        self.a = a
        self.b = b
        # …
        self._init_extra(*args, **kwargs)

    def _init_extra(self):
        """
        Subclasses can override this method to support extra
        __init__ arguments.
        """

        pass


class B(A):
    def _init_extra(self, w):
        self.w = w