В Python, как мне позвонить супер классом, когда это разовая namedtuple?
-
28-09-2019 - |
Вопрос
Таким образом, у меня есть большое количество классов полезных нагрузок для передачи сообщений для последовательного 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
От звонка, это все еще кажется, но почему? Как эти аргументы конструктору пропущены к NamedTuple? Это гарантировано или случайный результат того, как мро Установлено?
Если бы я хотел держаться подальше от Super, и сделайте это старым способом, есть ли, я могу получить доступ к именам, чтобы позвонить своему конструктору? Я бы предпочел не нужно сделать это:
DifferentialSpeed_ = namedtuple('DifferentialSpeed_',
'left_speed right_speed left_accel right_accel')
class DifferentialSpeed(Payload, DifferentialSpeed_):
Кажется видом излыбов и ненужным.
Какой мой лучший курс действий здесь?
Решение
Для начинающих, namedtuple(whatever)
наследует от tuple
, который неизбежен, и неизменные типы не беспокоятся __init__
, потому что к тому времени __init__
называется объект уже построен. Если вы хотите пройти аргументы namedtuple
Базовый класс, который вам придется переопределить __new__
вместо.
Вы можете увидеть определение результата namedtuple()
проходя в verbose=true
аргумент; Я нахожу это образовательным.
Другие советы
У вас есть три базовых классов: Payload
, Ваш NamedTuple. DifferentialSpeed_
, и общий базовый класс object
. Отказ Ни один из первых двух не имеет __init__
функция вообще, кроме того, что унаследована от object
. namedtuple
Не нужен __init__
, поскольку инициализация неизменных классов выполняется __new__
, который называется раньше __init__
бежит.
С super(DifferentialSpeed, self).__init__
разрешит к следующему __init__
в цепочке вызова, следующий __init__
является object.__init__
, что означает, что вы передаете аргументы этой функции. Это не ожидает никаких - нет причин, чтобы быть передающими аргументы object.__init__
.
(Раньше он принимал и молча игнорируют аргументы. Это поведение уходит - он ушел в Python 3 - именно поэтому вы получаете обесценение.)
Вы можете более четко вызвать проблему, добавив Payload.__init__
Функция, которая не требует аргументов. Когда вы пытаетесь пройти вместеkwargs., это поднимет ошибку.
Правильное, что нужно сделать в этом случае, почти наверняка удалить **kwargs
аргумент и просто позвонить super(DifferentialSpeed, self).__init__()
. Отказ Это не принимает никаких аргументов; DifferentialSpeed
проходит Payload
это собственный Аргументы это Payload
, и функционирует дальше по цепочке вызовов, ничего не знаешь.
Как указали другие, кортежи являются неизменным типом, который должен быть инициализирован в их __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)