В Python, как я могу вызвать суперкласс, если это одноразовый именованный набор? - PullRequest
13 голосов
/ 01 ноября 2010

Итак, у меня есть большое количество классов полезных сообщений для последовательного API, каждый из которых имеет несколько неизменяемых полей, метод синтаксического анализа и некоторые методы, которые являются общими.Я структурирую это так, что каждый будет наследовать от именованного кортежа для поведения полей и получать общие методы от родительского класса.Однако у меня возникли некоторые трудности с конструкторами:

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 из вызова,кажется, все еще работает, но почему?Как эти аргументы конструктора передаются в именованный кортеж?Это гарантировано, или случайный результат того, как устанавливается mro ?

Если бы я хотел держаться подальше от супер, и сделать это по-старому, есть ли какой-нибудь способ, которым я могу получить доступименованный кортеж для вызова своего конструктора?Я бы предпочел не делать этого:

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

Кажется многословным и ненужным.

Какой мой лучший способ действий здесь?

Ответы [ 3 ]

27 голосов
/ 01 ноября 2010

Для начала, namedtuple(whatever) наследуется от tuple, который является неизменным, а неизменяемые типы не беспокоятся о __init__, потому что к моменту вызова __init__ объект уже построен.Если вы хотите передать аргументы в базовый класс namedtuple, вам придется вместо этого переопределить __new__.

Определение результата namedtuple() можно увидеть, передав аргумент verbose=true;Я нахожу это образовательным.

5 голосов
/ 01 ноября 2010

У вас есть три базовых класса: Payload, ваш именованный кортеж DifferentialSpeed_ и общий базовый класс object.Ни у одного из первых двух нет функции __init__, за исключением функции, унаследованной от object.namedtuple не требуется __init__, поскольку инициализация неизменяемых классов выполняется с помощью __new__, который вызывается до запуска __init__.

, поскольку super(DifferentialSpeed, self).__init__ разрешается до следующего __init__ в цепочке вызовов, следующий __init__ будет object.__init__, что означает, что вы передаете аргументы этой функции.Он не ожидает ничего - нет причин передавать аргументы в object.__init__.

(Раньше он принимал и молча игнорировал аргументы. Такое поведение исчезает - оно исчезло в Python 3--вот почему вы получаете DeprecationWarning.)

Вы можете более четко вызвать проблему, добавив функцию Payload.__init__, которая не принимает аргументов.Когда вы попытаетесь передать `* kwargs , это вызовет ошибку.

В этом случае правильно будет удалить аргумент **kwargs и простоЗвоните super(DifferentialSpeed, self).__init__().Это не принимает никаких аргументов;DifferentialSpeed передает Payload свои собственные аргументы, которые Payload и функционируют дальше по цепочке вызовов, о которых ничего не знают.

3 голосов
/ 01 ноября 2010

Как уже отмечали другие, кортежи являются неизменным типом, который должен быть инициализирован в их __new__() вместо их __init__() метода - поэтому вам нужно добавить первый в ваш подкласс (и избавиться от последний). Ниже описано, как это будет применяться к вашему примеру кода. Единственным другим изменением было добавление оператора from import... в начало.

Примечание: cls должно быть передано дважды в вызове super() в __new__(), потому что это статический метод, хотя он имеет специальный случай, поэтому вам не нужно объявлять его быть одним.

from collections import namedtuple

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

class DifferentialSpeed(Payload, namedtuple('DifferentialSpeed_',
    'left_speed right_speed left_accel right_accel')):
    #### NOTE: __new__ instead of an __init__ method ####
    def __new__(cls, **kwargs):
        self = super(DifferentialSpeed, cls).__new__(cls, **kwargs)
        # TODO: Field verification
        print("foo")
        return self

    @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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...