Question

Alors, j'ai un grand nombre de classes de charge de message pour une API série, dont chacun a un certain nombre de champs immuables, une méthode d'analyse, et certaines méthodes qui sont partagées. La façon dont je suis la structuration est que chaque hériteront d'un namedtuple pour les comportements sur le terrain, et recevoir les méthodes communes d'une classe parente. Cependant, je vais avoir quelques difficultés avec les constructeurs:

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)

Cela fonctionne, mais je reçois l'avertissement suivant:

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

Si je retire **kwargs de l'appel, il semble encore au travail, mais pourquoi? Comment ces arguments au constructeur se passent par les namedtuple? Est-ce garanti, ou un résultat aléatoire de la façon dont le MRO soit établie?

Si je voulais rester loin de super et de le faire à l'ancienne, est-il un moyen que je peux accéder au namedtuple d'appeler son constructeur? Je préfère ne pas avoir à faire ceci:

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

Semble sorte de verbeux et inutile.

Quel est mon meilleur plan d'action ici?

Était-ce utile?

La solution

Pour commencer, hérite de namedtuple(whatever) de tuple, qui est immuable, et les types immuables ne se soucient pas __init__, parce que par le __init__ de temps est appelé l'objet est déjà construit. Si vous voulez passer des arguments à la classe de base de namedtuple vous devrez passer outre __new__ à la place.

Vous pouvez voir la définition du résultat de namedtuple() en passant dans un argument verbose=true; Je trouve l'éducation.

Autres conseils

Vous avez trois classes de base: Payload, votre namedtuple DifferentialSpeed_, et la classe de base commune object. Aucun des deux premiers ont une fonction de __init__ du tout, à l'exception de l'une héritée de object. namedtuple n'a pas besoin d'__init__, depuis l'initialisation des classes immuables se fait par __new__, qui est appelée avant __init__ est exécuté.

Depuis résorbe super(DifferentialSpeed, self).__init__ à la prochaine __init__ dans la chaîne d'appel, la prochaine __init__ est object.__init__, ce qui signifie que vous passez des arguments à cette fonction. Il ne prévoit pas -. Il n'y a aucune raison d'être des arguments à passer object.__init__

(Il permet d'accepter et ignorer en silence les arguments que le comportement va loin -. Il est parti en Python 3 -. Qui est la raison pour laquelle vous obtenez un DeprecationWarning)

Vous pouvez déclencher le problème plus clairement en ajoutant une fonction de Payload.__init__ qui ne prend aucun argument. Lorsque vous essayez de transmettre `* kwargs , il soulèverai une erreur.

La bonne chose à faire dans ce cas est presque certainement d'enlever l'argument **kwargs, et il suffit d'appeler super(DifferentialSpeed, self).__init__(). Il ne prend pas d'arguments; DifferentialSpeed passe Payload son propres arguments Payload et fonctions plus bas dans la chaîne d'appel, ne savent rien.

Comme d'autres l'ont souligné-out, sont un type tuples immuable, qui doit être initialisé dans leur __new__() au lieu de leur méthode de __init__() - donc vous devez ajouter l'ancien dans votre sous-classe (et se débarrasser de ce dernier). Ci-dessous, comment cela sera appliqué à votre code d'exemple. Le seul autre changement a été l'ajout d'une déclaration de from import... au début.

Remarque: cls doit être passé deux fois dans l'appel super() à __new__() car il est une méthode statique bien qu'il soit si vous spécial tubé ne devez pas déclarer être un

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)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top