In Python, come faccio io chiamo la super classe, quando si tratta di un'una tantum namedtuple?
-
28-09-2019 - |
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?
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)