Domanda

Quindi, ho un gran numero di classi messaggio carico utile per un API di serie, ognuno dei quali ha un numero di campi immutabili, un metodo di analisi, e alcuni metodi che sono condivisi. Il modo in cui io sono strutturare questo è che ogni erediterà da una namedtuple per i comportamenti di campo, e ricevere i metodi comuni da una classe genitore. Tuttavia, sto avendo qualche difficoltà con i costruttori:

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)

Questo funziona, ma ottengo il seguente avvertimento:

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

Se rimuovo **kwargs dalla chiamata, sembra ancora al lavoro, ma perché? Come vengono questi argomenti al costruttore sempre passati attraverso il namedtuple? E 'questo garantito, o il risultato casuale di come il MRO viene stabilito?

Se volevo stare lontano da super, e farlo alla vecchia maniera, c'è qualche modo posso accedere al namedtuple di chiamare il suo costruttore? Preferirei non hanno a che fare questo:

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

sembra tipo di verbose e inutile.

Qual è la mia migliore linea di azione qui?

È stato utile?

Soluzione

Per cominciare, eredita namedtuple(whatever) da tuple, che è immutabile, e tipi immutabili Non perdete tempo con __init__, perché per il momento __init__ è chiamato l'oggetto è già costruito. Se si vuole passare argomenti alla classe di base namedtuple dovrete ignorare __new__ invece.

È possibile visualizzare la definizione del risultato di namedtuple() passando un argomento verbose=true; Trovo educativo.

Altri suggerimenti

Sono disponibili tre classi di base: Payload, il tuo DifferentialSpeed_ namedtuple, e la classe object base comune. Nessuno dei primi due hanno una funzione __init__ affatto, ad eccezione di quello ereditato da object. namedtuple non ha bisogno di un __init__, dal momento che l'inizializzazione delle classi immutabili è fatto da __new__, che si chiama prima di __init__ viene eseguito.

Dato che risolve super(DifferentialSpeed, self).__init__ alla prossima __init__ nella catena di chiamate, il prossimo __init__ è object.__init__, il che significa che si sta passando argomenti per quella funzione. Essa non si aspetta alcuna -. Non c'è alcun motivo per essere il passaggio di argomenti a object.__init__

(E 'usato per accettare e silenziosamente ignorare gli argomenti che il comportamento è andando via -. È andato in Python. 3 - che è il motivo per cui si ottiene un DeprecationWarning)

Si può attivare il problema in modo più chiaro con l'aggiunta di una funzione Payload.__init__ che non accetta argomenti. Quando si tenta di passare lungo `* kwargs , che sarà genera un errore.

La cosa giusta da fare in questo caso è quasi certamente di rimuovere l'argomento **kwargs, e basta chiamare super(DifferentialSpeed, self).__init__(). Non ci vuole alcun argomento; DifferentialSpeed sta passando Payload sua proprio argomenti che Payload, e le funzioni a valle della catena di chiamata, non sanno nulla.

Come altri hanno sottolineato-out, tuple sono un tipo immutabile, che deve essere inizializzato nella loro __new__() invece che il loro metodo __init__() - quindi è necessario aggiungere l'ex nella sottoclasse (e di sbarazzarsi di questi ultimi). Qui di seguito è come questo possa essere applicato al vostro codice di esempio. L'unico altro cambiamento è stato l'aggiunta di una dichiarazione from import... all'inizio.

Nota: cls deve essere passato per due volte nella chiamata super() in __new__() perché è un metodo statico anche se è speciale con carter in modo da non dover dichiarare di essere uno

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)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top