Pergunta

Portanto, tenho um grande número de classes de carga útil de mensagem para uma API serial, cada uma delas com vários campos imutáveis, um método de análise e alguns métodos que são compartilhados.A maneira como estou estruturando isso é que cada um herdará de uma tupla nomeada para os comportamentos do campo e receberá os métodos comuns de uma classe pai.Porém, estou tendo algumas dificuldades com os construtores:

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)

Isso funciona, mas recebo o seguinte aviso:

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

Se eu remover **kwargs da ligação, ainda parece funcionar, mas por quê?Como esses argumentos do construtor são passados ​​para o nomeadotuple?Isso é garantido ou é um resultado aleatório de como o mro se estabelece?

Se eu quisesse ficar longe do super e fazer isso da maneira antiga, existe alguma maneira de acessar o nomeadotuple para chamar seu construtor?Prefiro não ter que fazer isso:

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

Parece meio prolixo e desnecessário.

Qual é o meu melhor curso de ação aqui?

Foi útil?

Solução

Para iniciantes, namedtuple(whatever) herda de tuple, que é imutável, e tipos imutáveis ​​não se preocupam com __init__, porque na hora __init__ é chamado o objeto já está construído.Se você quiser passar argumentos para o namedtuple classe base você terá que substituir __new__ em vez de.

Você pode ver a definição do resultado de namedtuple() passando em um verbose=true argumento;Acho isso educativo.

Outras dicas

Você tem três classes base: Payload, seu nomeadotuple DifferentialSpeed_, e a classe base comum object.Nenhum dos dois primeiros tem __init__ função, exceto aquela herdada de object. namedtuple não precisa de um __init__, já que a inicialização de classes imutáveis ​​é feita por __new__, que é chamado antes __init__ é executado.

Desde super(DifferentialSpeed, self).__init__ resolve para o próximo __init__ na cadeia de chamadas, o próximo __init__ é object.__init__, o que significa que você está passando argumentos para essa função.Ele não espera nada - não há razão para passar argumentos para object.__init__.

(Costumava aceitar e ignorar argumentos silenciosamente.Esse comportamento está desaparecendo - desapareceu no Python 3 - e é por isso que você recebe um DeprecationWarning.)

Você pode desencadear o problema de forma mais clara adicionando um Payload.__init__ função que não aceita argumentos.Quando você tenta passar adiante `*kwargs, isso gerará um erro.

A coisa correta a fazer neste caso é quase certamente remover o **kwargs argumento, e apenas ligue super(DifferentialSpeed, self).__init__().Não são necessários argumentos; DifferentialSpeed está passando Payload isso é ter argumentos que Payload, e funções mais abaixo na cadeia de chamadas, não sabemos nada sobre.

Como outros apontaram, as tuplas são um tipo imutável, que deve ser inicializado em seu __new__() em vez de seus __init__() método - então você precisa adicionar o primeiro em sua subclasse (e se livrar do último).Abaixo está como isso seria aplicado ao seu código de exemplo.A única outra mudança foi adicionar um from import... declaração para o início.

Observação: cls tem que ser passado duas vezes no super() ligue __new__() porque é um método estático, embora tenha um caso especial, então você não precisa declará-lo como um.

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)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top