【问题标题】:Inheriting from a namedtuple base class从命名元组基类继承
【发布时间】:2017-07-12 04:09:39
【问题描述】:

这个问题与 Inherit namedtuple from a base class in python 相反,其目的是从命名元组继承子类,反之亦然。

在正常继承中,这是可行的:

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


class Z(Y):
    def __init__(self, a, b, c, d):
        super(Z, self).__init__(a, b, c)
        self.d = d

[出]:

>>> Z(1,2,3,4)
<__main__.Z object at 0x10fcad950>

但是如果基类是namedtuple:

from collections import namedtuple

X = namedtuple('X', 'a b c')

class Z(X):
    def __init__(self, a, b, c, d):
        super(Z, self).__init__(a, b, c)
        self.d = d

[出]:

>>> Z(1,2,3,4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __new__() takes exactly 4 arguments (5 given)

问题,是否可以继承命名元组作为 Python 中的基类?是这样,怎么样?

【问题讨论】:

  • 这并不完全是您问题的答案,但可能值得一试新的 python dataclasses。在大多数情况下,您将覆盖命名元组,您可能希望使用它们。

标签: python oop inheritance super namedtuple


【解决方案1】:

不要严格考虑继承,因为namedtuple是一个函数,另一种方法是将它封装在一个新函数中。

然后问题就变成了:“我们如何构造一个默认具有属性a, b, c 的命名元组,以及可选的一些附加属性?”

def namedtuple_with_abc(name, props=[]):
     added = props if type(props) == type([]) else props.split()
     return namedtuple(name, ['a', 'b', 'c'] + added)

X = namedtuple_with_abc('X')
Z = namedtuple_with_abc('Z', 'd e')

>>> X(1, 2, 3)
X(a=1, b=2, c=3)

>>> Z(4, 5, 6, 7, 8)
Z(a=4, b=5, c=6, e=7, f=8)

【讨论】:

    【解决方案2】:

    两年后,我带着完全相同的问题来到这里。
    我个人认为@property 装饰器更适合这里:

    from collections import namedtuple
    
    class Base:
        @property
        def computed_value(self):
            return self.a + self.b + self.c
    
    # inherits from Base
    class A(Base, namedtuple('A', 'a b c')):
        pass
    
    cls = A(1, 2, 3)
    print(cls.computed_value)
    # 6
    

    【讨论】:

    【解决方案3】:

    我认为你可以通过包含所有字段来实现你想要的 在原始命名元组中,然后调整参数的数量 使用__new__,正如上面 schwobaseggl 所建议的那样。例如,要解决 max 的情况,其中一些输入值将被计算 而不是直接提供,以下工作:

    from collections import namedtuple
    
    class A(namedtuple('A', 'a b c computed_value')):
        def __new__(cls, a, b, c):
            computed_value = (a + b + c)
            return super(A, cls).__new__(cls, a, b, c, computed_value)
    
    >>> A(1,2,3)
    A(a=1, b=2, c=3, computed_value=6)
    

    【讨论】:

      【解决方案4】:

      你可以,但你必须覆盖__new__,它在__init__之前被隐式调用:

      class Z(X):
        def __new__(cls, a, b, c, d):
          self = super(Z, cls).__new__(cls, a, b, c)
          self.d = d
          return self
      
      >>> z = Z(1, 2, 3, 4)
      >>> z
      Z(a=1, b=2, c=3)
      >>> z.d
      4
      

      d 将只是一个独立的属性!

      >>> list(z)
      [1, 2, 3]
      

      【讨论】:

      • 可以self.append(d)吗?
      • @alvas 不,因为namedtuple 基本上是tuple(设计上是不可变的,因此没有append
      • @Mseifert 谢谢!但是 =(
      • 是从namedtuple 继承的反模式,或者如果我只需要一个 coupe 额外的参数,我想与其他参数分开,这是一种合理的方法? (具体来说,我希望它们在__init__ 中计算,而不是在用户代码中分配。)
      猜你喜欢
      • 2017-12-10
      • 2010-11-26
      • 1970-01-01
      • 2019-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-21
      相关资源
      最近更新 更多