In Python, wie rufe ich die super-Klasse, wenn es ein one-off namedtuple?
So, ich habe eine große Anzahl von message-Payload-Klassen für eine serielle API, von denen jeder eine Anzahl von unveränderlich Felder einem parse-Methode, und einige Methoden, die freigegeben sind. Die Art, wie ich bin Strukturierung ist, dass die einzelnen Erben aus namedtuple für den Bereich Verhalten, und Sie erhalten die gemeinsamen Methoden von einer übergeordneten Klasse. Aber, ich habe einige Schwierigkeiten mit den Konstruktoren:
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)
Dies funktioniert, aber ich bekomme die folgende Warnung:
DeprecationWarning: object.__init__() takes no parameters
super(DifferentialSpeed, self).__init__(**kwargs)
Wenn ich entfernen **kwargs
von der Aufforderung, es scheint immer noch zu funktionieren, aber warum? Wie sind diese Argumente dem Konstruktor übergeben überholt durch die namedtuple? Ist dies gewährleistet, oder ein zufälliges Ergebnis, wie die mro wird gegründet?
Wenn ich wollte, zu bleiben Weg von super, und machen es auf die alte Art und Weise, gibt es eine Möglichkeit, ich kann auf die namedtuple zu Ihr Konstruktor aufgerufen? Ich würde lieber nicht haben, um dies zu tun:
DifferentialSpeed_ = namedtuple('DifferentialSpeed_',
'left_speed right_speed left_accel right_accel')
class DifferentialSpeed(Payload, DifferentialSpeed_):
Scheint Art von verbose und unnötig.
Was ist meine beste Vorgehensweise hier?
- Beachten Sie, dass, wenn Sie versuchen, zu verwenden
namedtuple
Speicher zu speichern, müssen Sie__slots__ = ()
in der abgeleiteten Klasse als auch die anderen übernommenenPayload
Klasse, oder die Klasse noch eine__dict__
.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Für den Anfang
namedtuple(whatever)
erbt vontuple
, die ist unveränderlich, und unveränderlich Typen nicht die Mühe mit__init__
, weil durch die Zeit, die__init__
aufgerufen wird, das Objekt wird bereits gebaut. Wenn Sie wollen Argumente an dienamedtuple
Basisklasse müssen Sie überschreiben__new__
statt.Können Sie sehen, die definition der Ergebnis
namedtuple()
durch die übergabeverbose=true
argument; ich finde es lehrreich.Haben Sie drei Basis-Klassen:
Payload
Ihre namedtupleDifferentialSpeed_
, und die gemeinsame Basis-Klasseobject
. Keiner von den ersten beiden haben ein__init__
Funktion, außer für den einen geerbt vonobject
.namedtuple
braucht keine__init__
, da die Initialisierung, unveränderliche Klassen erfolgt durch__new__
, die aufgerufen wird, bevor__init__
ausgeführt.Seit
super(DifferentialSpeed, self).__init__
löst den nächsten__init__
in der Aufruf-Kette, die neben__init__
istobject.__init__
, was bedeutet, dass Sie vorbei sind Argumente zu dieser Funktion. Es gar nicht erwarten--es gibt keinen Grund, sich zu übergeben von Argumenten anobject.__init__
.(Es verwendet wird, zu akzeptieren und stillschweigend ignorieren Argumente. Dieses Verhalten ist Weg--Weg ist es in Python 3-das ist, warum Sie bekommen eine DeprecationWarning.)
Ausgelöst werden kann, das problem deutlicher, indem ein
Payload.__init__
Funktion nimmt keine Argumente an. Wenn Sie versuchen, um pass entlang `*kwargs, es wird ein Fehler ausgelöst.Die richtige Sache zu tun, in diesem Fall ist es fast sicher zu entfernen, die
**kwargs
argument, und rufen Siesuper(DifferentialSpeed, self).__init__()
. Es nimmt keine Argumente;DifferentialSpeed
ist vorbeiPayload
seine eigenen Argumente, diePayload
und Funktionen, die weiter unten in der call-Kette, nichts wissen.**kwargs
argument, und rufen Siesuper(DifferentialSpeed, self).__init__(**kwargs)
" scheint widersprüchlich, sollte nicht der Letzte Teil sein "rufensuper(DifferentialSpeed, self).__init__()
"?__new__
Objekte/typobject.c. (Ich habe keine Lust, ins detail zu gehen, seit der OP ignoriert diese Antwort vollständig aus und akzeptiert man, dass nicht einmal Antwort auf seine Frage...)__new__()
produziert Warnungen über die Argumente sind in der Tat kompliziert. Für alle, die Interesse haben, können Sie eine Kopie der nur die relevanten Kommentar vontypecobject.c
hier oder den gesamten source-Datei im svn hier.Wie andere haben darauf hingewiesen-out, Tupel sind unveränderlich geben, die initialisiert werden müssen, die in Ihrer
__new__()
anstatt Ihre__init__()
Methode-so müssen Sie den ehemaligen in Ihrer Unterklasse (und loszuwerden des letzteren). Unten ist, wie diese, angewendet auf dein Beispiel-code. Die einzige andere änderung war das hinzufügen einesfrom import...
- Anweisung an den Anfang.Hinweis:
cls
übergeben werden zweimal in dersuper()
Anruf in__new__()
weil es eine statische Methode, obwohl es special-cased, so dass Sie nicht haben, um es erklären zu sein.__new__
. Er würde Sie nur tun müssen, dass, wenn er wollte, ändern Sie die Argumente, dieDifferentialSpeed_
initialisiert; er ist nur eine überprüfung.__new__()
würde beteiligt werden müssen, damit die Argumente verifiziert werden konnte, bevor Sie zugeordnet wurden, um das Tupel (denn Sie kann nicht später geändert werden). z.B. könnten Sie gegeben werden default-Werte oder eine Ausnahme ausgelöst, ohne falsche Zuordnungen gemacht.__init__
funktioniert genauso gut. (Im hintergrund zwingt einen Standard, hört sich nicht gesund Verhalten.)