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

21 投票
4 回答
10441 浏览
提问于 2025-04-15 18:59

我有一个类:

class A(object):
    def __init__(self,a,b,c,d,e,f,g,...........,x,y,z)
        #do some init stuff

还有一个子类,它需要一个额外的参数(就是最后那个 W)。

class B(A):
    def __init__(self.a,b,c,d,e,f,g,...........,x,y,z,W)
        A.__init__(self,a,b,c,d,e,f,g,...........,x,y,z)
        self.__W=W

写这么多重复的代码感觉很傻,比如把 B 的构造函数里的所有参数都传给 A 的构造函数。因为这样的话,每次修改 A 的构造函数时,就得在 B 的代码里改两个地方。

我在想,Python 里应该有一些方法可以处理这种情况,但我不知道。你能给我指个方向吗?

我最好的想法是给 A 做一个类似复制构造函数的东西,然后把 B 的代码改成:

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

这样做能满足我的需求,因为我每次都是在给定父类的实例时创建子类。不过我不确定这样是否可行……

4 个回答

2

在某些情况下,如果传给 __init__ 方法的参数有默认值,那么在子类中就可以避免重复写 __init__ 方法的参数列表。

在这种情况下,__init__ 方法可以把额外的参数传给另一个方法,而子类可以选择重写这个方法:

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
7

编辑:根据Matt的建议,并考虑到gnibbler对位置参数方法的担忧;你可能需要检查一下,确保额外的子类特定参数被正确指定——这和Alex的回答类似:

class B(A):
  def __init__(self, *args, **kwargs):
    try:
      self._w = kwargs.pop('w')
    except KeyError:
      pass
    super(B,self).__init__(*args, **kwargs)

>>> b = B(1,2,w=3)
>>> b.a
1
>>> b.b
2
>>> b._w
3

原始回答:
Matt的回答的思路一样,只是用super()来代替。

使用super()来调用父类的__init__()方法,然后继续初始化子类:

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
21

考虑到参数可以通过名称或者位置来传递,我会这样写代码:

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

撰写回答