En Python, Como llamar en la super clase cuando es un hecho aislado namedtuple?
-
28-09-2019 - |
Pregunta
Por lo tanto, tiene un gran número de clases de mensaje de carga útil para una serie de API, cada uno de los cuales tiene una serie de campos inmutables, un método de análisis y algunos métodos que son compartidos. La forma en que estoy estructuración esto es que cada uno va a heredar de una namedtuple de los comportamientos de campo, y recibir los métodos comunes de una clase padre. Sin embargo, estoy teniendo algunas dificultades con los constructores:
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)
Esto funciona, pero me sale el siguiente aviso:
DeprecationWarning: object.__init__() takes no parameters
super(DifferentialSpeed, self).__init__(**kwargs)
Si quito **kwargs
de la llamada, todavía parece funcionar, pero ¿por qué? ¿Cómo están esos argumentos al constructor conseguir pasar a través de la namedtuple? Se esta garantizado, o un resultado aleatorio de cómo el MRO se establezca?
Si quería mantenerse alejado de súper, y hacerlo de la manera antigua, ¿hay alguna manera de que pueda acceder al namedtuple a llamar a su constructor? Yo preferiría no tener que hacer esto:
DifferentialSpeed_ = namedtuple('DifferentialSpeed_',
'left_speed right_speed left_accel right_accel')
class DifferentialSpeed(Payload, DifferentialSpeed_):
Parece un poco prolijo e innecesaria.
¿Cuál es mi mejor curso de acción aquí?
Solución
Para empezar, hereda namedtuple(whatever)
de tuple
, que es inmutable, y los tipos inmutables no se molestan con __init__
, ya que por el momento __init__
se llama el objeto ya está construido. Si quiere pasar argumentos a la clase base namedtuple
que tendrá que reemplazar __new__
lugar.
Se puede ver la definición del resultado de namedtuple()
pasando un argumento verbose=true
; Lo encuentro educativo.
Otros consejos
Hay tres clases básicas: Payload
, su DifferentialSpeed_
namedtuple, y el object
clase base común. Ninguno de los dos primeros tienen una función __init__
en absoluto, excepto por el uno heredado de object
. namedtuple
no necesita una __init__
, ya que la inicialización de las clases inmutables se realiza mediante __new__
, que se llama antes de ejecutar __init__
.
Desde resuelve super(DifferentialSpeed, self).__init__
a la siguiente __init__
en la cadena de llamadas, el siguiente es __init__
object.__init__
, lo que significa que está pasando argumentos para esa función. Que no espera ninguna -. No hay razón para ser pasar argumentos a object.__init__
(Se utiliza para aceptar y silenciosamente ignorar los argumentos de que el comportamiento se va -. Que se ha ido en Python 3 -. Por lo que se obtiene una DeprecationWarning)
Se puede desencadenar el problema con mayor claridad mediante la adición de una función Payload.__init__
que no tiene argumentos. Cuando intenta pasar a lo largo `* kwargs , que va a elevar un error.
Lo que se debe hacer en este caso es casi seguro para eliminar el argumento **kwargs
, y simplemente llamar super(DifferentialSpeed, self).__init__()
. No se necesita ningún argumento; DifferentialSpeed
está pasando Payload
su propios argumentos que Payload
y funciones más abajo en la cadena de llamadas, no saben nada acerca de.
Como otros han señalado de salida, tuplas son un tipo inmutable, que debe ser inicializado en su __new__()
en lugar de su método __init__()
- por lo que es necesario agregar la antigua en la subclase (y deshacerse de este último). A continuación se muestra cómo podría ser aplicado a su código de ejemplo. El otro cambio fue la adición de una declaración from import...
al principio.
Nota: cls
tiene que pasar dos veces en la llamada super()
en __new__()
porque es un método estático aunque es especial con carcasa por lo que no tiene que declarar que es 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)