在Python中,当它是一个一次性的命名元组时,如何调用超类?

时间:2010-11-01 18:12:39

标签: inheritance python-3.x multiple-inheritance super namedtuple

因此,我有一个串行API的大量消息Payload类,每个类都有许多不可变字段,一个解析方法和一些共享的方法。我构造它的方式是每个都将从字段行为的namedtuple继承,并从父类接收公共方法。但是,我在构造函数方面遇到了一些困难:

class Payload:
    def test(self):
        print("bar")

class DifferentialSpeed(Payload, namedtuple('DifferentialSpeed_', 
    'left_speed right_speed left_accel right_accel')):
    __slots__ = ()
    def __init__(self, **kwargs):
        super(DifferentialSpeed, self).__init__(**kwargs)
        # TODO: Field verification
        print("foo")

    @classmethod
    def parse(self, raw):
        # Dummy for now
        return self(left_speed = 0.0, right_speed = 0.1,
                    left_accel = 0.2, right_accel = 0.3)

    def __str__(self):
        return "Left Speed: %fm/s\nRight Speed: %fm/s\n"\
            "Left Acceleration: %fm/s^2\nRight Acceleration: %fm/s^2" % (
            self.left_speed, self.right_speed, self.left_accel, self.right_accel)


payload = DifferentialSpeed.parse('dummy')
print(payload)

这样可行,但我收到以下警告:

DeprecationWarning: object.__init__() takes no parameters
  super(DifferentialSpeed, self).__init__(**kwargs)

如果我从通话中删除**kwargs,它似乎仍然有效,但为什么?如何将构造函数的参数传递给namedtuple?这是保证,还是 mro 如何建立的随机结果?

如果我想远离超级,并以旧的方式做,有什么方法可以访问namedtuple来调用它的构造函数?我宁愿不必这样做:

DifferentialSpeed_ = namedtuple('DifferentialSpeed_', 
    'left_speed right_speed left_accel right_accel')
class DifferentialSpeed(Payload, DifferentialSpeed_):

似乎有点冗长和不必要。

我最好的行动方式是什么?

3 个答案:

答案 0 :(得分:27)

对于初学者,namedtuple(whatever)继承自tuple,这是不可变的,不可变类型不会打扰__init__,因为当__init__被称为对象时已经建成了。如果要将参数传递给namedtuple基类,则必须覆盖__new__

您可以通过传递namedtuple()参数来查看verbose=true结果的定义;我发现它很有教育意义。

答案 1 :(得分:5)

您有三个基类:Payload,您的namedtuple DifferentialSpeed_和公共基类object。前两个都没有__init__功能,除了从object继承的功能。 namedtuple不需要__init__,因为不可变类的初始化由__new__完成,__init__super(DifferentialSpeed, self).__init__运行之前调用。

由于__init__解析为调用链中的下一个__init__,因此下一个object.__init__object.__init__,这意味着您将参数传递给该函数。它没有任何期望 - 没有理由将参数传递给Payload.__init__

(它过去常常接受并默默地忽略参数。这种行为正在消失 - 它已经在Python 3中消失 - 这就是为什么你会得到一个弃用警告。)

您可以通过添加不带参数的**kwargs函数来更清楚地触发问题。当你试图传递`* kwargs 时,它会引发错误。

在这种情况下,正确的做法几乎肯定是删除super(DifferentialSpeed, self).__init__()参数,只需调用DifferentialSpeed即可。它没有任何论据; Payload正在传递Payload自己的参数{{1}},并在调用链中进一步运行,对此一无所知。

答案 2 :(得分:3)

正如其他人指出的那样,元组是一个不可变类型,必须在__new__()而不是__init__()方法中初始化 - 所以你需要在你的子类中添加前者(和摆脱后者)。以下是如何将其应用于您的示例代码。唯一的另一个变化是在开头添加from import...语句。

注意: cls必须在super()的{​​{1}}来电中传递两次,因为它是一种静态方法,尽管它是特殊的,所以你不要不得不声明它是一个。

__new__()