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í?

¿Fue útil?

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)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top