Gast
#6670832
Hallo, kann man herausfinden, ob eine Variable von der Childclass oder Parent kommt?
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
|
Anzeige
|
Python Variable von Childclass
Gast
#6670832
Hallo, kann man herausfinden, ob eine Variable von der Childclass oder Parent kommt?
Gast
#6670891
in x.__dict__ sind nur die Elemente von Student. x.__class__.mro() zeigt dann, wie weiter gesucht wird, wenn die Variable nicht in _dict_ ist. Aber die eigentliche Frage ist: Warum willst du das herausfinden? In normalen Programmen muss man das nicht wissen. Hi Dirk, du bekommst zwar mit str(x.printname) heraus wo deine methode verortet ist, aber wo deine variable angesiedelt ist ist wegen des eigenen namespaces scheinbar nicht ermittelbar, siehe: https://stackoverflow.com/questions/3648564/python-subclass-access-to-class-variable-of-parent Gruß Gerd
Gast
#6670925
>Aber die eigentliche Frage ist: Warum willst du das herausfinden? >In normalen Programmen muss man das nicht wissen. Ich würde gerne die Klassen einzeln serialzieren, deshalb wollte ich prüfen, woher die Variable kommt.
Gast
#6670936
Dirk schrieb: > serialzieren Ok. Das ist eines der wenigen Einsatzzwecke von mro(). Allerdings gibt es bereits einen eingebauten (de-)serialisierer. Er heißt pickle.
Gast
#6670962
Pickle hatte ich mir schon angeschaut, aber wenn ich es richtig verstehe geht nur JSON und ich nicht C-Datentypen.
Gast
#6670964
Dirk schrieb: > Pickle hatte ich mir schon angeschaut, aber wenn ich es richtig verstehe > geht nur JSON und ich nicht C-Datentypen. Es geht alles, was Python ist.
Gast
#6671610
MaWin schrieb: > Dirk schrieb: >> Pickle hatte ich mir schon angeschaut, aber wenn ich es richtig verstehe >> geht nur JSON und ich nicht C-Datentypen. > > Es geht alles, was Python ist. Leider nicht. Es gibt ein externes Modul namens "dill", das deutlich mehr kann als das Standardmodul "pickle", aber zum Beispiel mit Generatoren kann dill leider auch nicht umgehen.
Gast
#6671614
Dirk schrieb: >
Nein, so bekommst Du nur die Variablen aus x.__dict__. Alle Variablennamen kannst Du mit dir(x) holen und dann ein dict() draus machen:
>
Mußt Du nicht, sowas einfaches können das Standardmodul "pickle" und das externe Modul "dill" problemlos händeln... und beide sind im Zweifelsfall schneller, effizienter und fehlerärmer als etwas Selbstgebasteltes.
Gast
#6671920
Irgendwie finde ich keine Information das Pickle & Dill C Typen (uint8, uint16, etc.) unterstützt. >dill can pickle the following standard types: > >none, type, bool, int, long, float, complex, str, unicode,
Gast
#6671937
Dirk schrieb: > Irgendwie finde ich keine Information das Pickle & Dill C Typen (uint8, > uint16, etc.) unterstützt. Was soll dieser Satz überhaupt für ein Python-Programm bedeuten? Kannst du bitte mal ein Beispiel liefern?
Gast
#6672044
Dirk schrieb: > Irgendwie finde ich keine Information das Pickle & Dill C Typen (uint8, > uint16, etc.) unterstützt. Irgendwie sehe ich in Deinem Code gar keine "C Typen". Kannst Du bitte mal kurz erklären, was Du vorhast, was Du mit diesen "C Typen" überhaupt tun willst, und warum Du "pickle" und / oder "dill" nicht einfach mal (mit Deinen eigenen Klassen oder Instanzen) ausprobierst? Das kostet nur zwei, drei Zeilen Code...
Gast
#6672590
>Irgendwie sehe ich in Deinem Code gar keine "C Typen". Kannst Du bitte >mal kurz erklären, was Du vorhast, was Du mit diesen "C Typen" überhaupt >tun willst, und warum Du "pickle" und / oder "dill" nicht einfach mal >(mit Deinen eigenen Klassen oder Instanzen) ausprobierst? Das kostet nur >zwei, drei Zeilen Code... Da hast Du natürlich recht. Ich muss die einzelnen Variablen in C Typen (int8, uint8, int16, uint16, uint32, int32, uint64, int64) serialisieren und dann per TCP an eine nicht veränderbare Anwendung senden, deshalb scheint mir Pickle und Dill keine alternative zusein. Ich benutze dafür momentan das Struct Modul und wollte nun erkennen ob die Variablen aus der Childklasse kommen oder Parentklasse, weil ich entsprechend der Klasse noch ein paar zusätzliche Daten mitgeben muss. Dirk schrieb: > Da hast Du natürlich recht. Ich muss die einzelnen Variablen in C Typen > (int8, uint8, int16, uint16, uint32, int32, uint64, int64) serialisieren > und dann per TCP an eine nicht veränderbare Anwendung senden, Bitte sei nicht böse, wenn ich Dir an dieser Stelle eine kleine Ermahnung leider nicht ersparen kann: wenn Du das von vorneherein gesagt hättest, dann hätte Dir zweifellos viel schneller geholfen werden können. > deshalb scheint mir Pickle und Dill keine alternative zusein. Das stimmt, Pickle und Dill benutzen ein eigenes Datenformat, das für den Austausch von Daten mit anderen Programmiersprachen nicht vorgesehen ist. > Ich benutze dafür > momentan das Struct Modul und wollte nun erkennen ob die Variablen aus > der Childklasse kommen oder Parentklasse, weil ich entsprechend der > Klasse noch ein paar zusätzliche Daten mitgeben muss. Ja, das stimmt, struct.Struct ist eine Möglichkeit. Allerdings... wenn ich das richtig verstehe, muß ohnehin jede Deiner Klassen wissen, welche Eigenschaft sie bei der Serialisierung in welchen C-Datentyp umwandeln muß. Denn ein int() in Python kann ja viele C-Datentypen behinahlten, von einer uint8 bis hin zu einer int64. Insofern verstehe ich noch nicht ganz, was dann die dynamische Auflösung bewirken soll. Zudem brauchst Du die Information, welche Eigenschaft zu welcher Klasse gehört, ja nur bei der Deserialisierung, aber die macht doch in Wahrheit Dein unveränderliches Serverprogramm? Wie dem auch sei, möchte ich an dieser Stelle ein etwas... unbekanntes Feature von Python erinnern -- das "magische" Klassenattribut "__slots__". Das bewirkt, daß die Autovivikation einer Klasse ausgeschaltet wird und der Klasse nurmehr Eigenschaften zugewiesen werden können, die in "__slots__" aufgeführt sind:
ist valider Python-Code und funktioniert natürlich genau so wie erwartet: durch die Zuweisung an a.eins wird eine Eigenschaft "eins" in der Instanz "a" unserer Klasse erzeugt und ihr der Wert 1 zugewiesen. Dasselbe funktioniert übrigens nicht nur mit Instanz-, sondern auch mit Klasseneigenschaften... Genau dem kann man entgegenwirken, indem man der Klasseneigenschaft "__slots__" eine Liste mit erlaubten Namen von Eigenschaften zuweist:
Eigentlich ist "__slots__" zur Verbesserung der Speichereffizienz gedacht und sollte nicht für Einschränkungen benutzt werden, aber... genau: "sollte nicht" heißt nicht "darf nicht". Zu Beachten ist allerdings, daß die Klasseneigenschaft "__slots__" nicht automatisch vererbt wird, siehe dazu auch das kleine Skript im Anhang mit einer generischen Serialisierungsmethode -- YMMV, HTH. Nebenbei möchte ich aber noch auf zwei andere Ideen hinweisen, wie sich das von Dir gewünschte bewerkstelligen ließe. Zum Einen wäre das ein kompiliertes Shared Object (Win: DLL), das seine Parameter über das Python-Standardmodul "ctypes" übergeben bekommt und dann wahlweise die serialisierte Bytefolge zurückgibt oder ihrerseits direkt den Server anspricht. Eine andere Möglichkeit wäre es -- wenn Du etwas auf dem Server installieren kannst -- einen kleines Reverse-Proxy-Programm zu schreiben, das die Daten in einem beliebigen Format wie zum Beispiel JSON entgegennimmt, passend wandelt, und sie dann über localhost an Dein Serverprogramm übergibt. Der Vorteil wäre, daß Deine Client-Logik recht einfach gehalten werden kann und Du die Daten einfach mithilfe des json-Moduls von Python serialisieren, mit einem der Standardmodule urllib oder urllib3 oder dem Erweiterungsmodul requests per HTTP an einen simplen HTTP-Server auf der Basis von Flask oder http.server.HTTPServer übergeben kannst. Zuletzt möchte ich noch auf zwei Kleinigkeiten hinweisen, die mir an Deinem Code aufgefallen sind, nämlich die "magische" Methoden __repr__() und __str__() sowie die Abfrage, ob Dein Code als Programm oder als Modul geladen worden ist. Die magische Methode __str__() wird immer dann aufgerufen, wenn Deine Instanz als String repräsentiert werden soll. Die Funktion print() macht das automatisch, ebenso eine explizierte Konvertierung mit str(). Die magische Methode __repr__() tut etwas sehr Ähnliches, soll aber eine String-Repräsentation der Instanz zurückgeben, aus der die Instanz wiederhergestellt werden kann -- in der Praxis funktioniert das allerdings leider häufig nicht so gut. __repr__() wird auch immer dann aufgerufen, wenn eine __str__()-Methode nicht vorhanden ist oder wenn diese Repräsentation explizit mit der Builtin-Funktion repr() angefordert wird. Die Codezeile
fragt ab, ob Dein Code als Programm ablaufen soll -- also: mit ./programm.py gestartet wurde. Wenn dies der Fall ist, wird der Code im If-Block ausgeführt. Sofern Dein Code hingegen von einem anderen Python-Programm mit "import" oder einem ähnlichen Mechanismus geladen wurde, wird der Code nicht ausgeführt. Das ist aus zweierlei Gründen praktisch: einerseits kannst Du in dem if-Block ganz einfach simple Unittests und / oder Beispielcode unterbringen, andererseits kannst Du Deine Klassen und Funktionen von anderem Python-Code aus laden und benutzen, ohne daß der If-Block ausgeführt wird. Ich zum Beispiel nutze dieses Feature gerne in Verbindung mit Datenbanken und den OR-Mappern SQLObject und SQLAlchemy: wird mein Code als Programm ausgeführt, werden die Tabellen alle gelöscht, neu angelegt, und Beispieldaten zum Testen eingetragen, während mein Code als Modul geladen, stellt er die Datenbankabstraktion zur Verfügung. Edit: Anhang vergessen... :-)
Gast
#6673192
>Bitte sei nicht böse, wenn ich Dir an dieser Stelle eine kleine >Ermahnung leider nicht ersparen kann: wenn Du das von vorneherein gesagt >hättest, dann hätte Dir zweifellos viel schneller geholfen werden >können. Bin ich nicht. Ich hatte mich etwas zu stark versteift auf mein Problem ohne das drum herum zuerklären. Ich muss die Daten nicht nur serialsieren, sondern auch empfangende Bytearrays wieder deserialsieren zu der entsprechen Klasse. Der Tipp mit den _slots_ und mro sind schon sehr hilfreich und wie erwähnt für eine Klasse welche keine Vererbung hat klappt es auch wunderbar. Jetzt heisst es die Vorschläge genauer zu verstehen und ein Testprojekt zuerstellen. Python bietet extrem viele Möglichkeiten, aber mir fehlt ein bisschen wie bei Qt das Metasystem, weil darüber kommt man relativ leicht an die benötigten Information, aber anscheinend bietet Python hier auch schon einiges. Vielleicht kannst du dir dann eines der gängigen Serialisierungsframeworks anschauen wie Protobuf, Flatbuf oder Cap'n Proto. Die kannst du mit allen möglichen Sprachen verwenden. Dirk schrieb: > Bin ich nicht. Prima, Danke! :-) > Ich muss die Daten nicht nur serialsieren, sondern auch empfangende > Bytearrays wieder deserialsieren zu der entsprechen Klasse. Der Tipp mit > den _slots_ und mro sind schon sehr hilfreich und wie erwähnt für eine > Klasse welche keine Vererbung hat klappt es auch wunderbar. Jetzt heisst > es die Vorschläge genauer zu verstehen und ein Testprojekt zuerstellen. Hmmm... also, ehrlich gesagt habe ich die Befürchtung, daß wir bezüglich meines Hinweises auf die Detailtiefe noch nicht wirklich weitergekommen sind. Klar, Du möchtest es verstehen, und ich möchte Dir wirklich gerne helfen, aber hier sind noch so viele Fragezeichen... Ich frag' einfach mal so ein paar Dinge ins Blaue hinein, die mich interessieren würden, okay? Wenn Du zu irgendeinem Teil davon nichts sagen kannst oder möchtest, ist das natürlich völlig okay, aber mit mehr Informationen hätte ich vielleicht noch mehr gute Ideen... Du schreibst weiter oben, daß es eine Gegenseite mir einer "nicht veränderbaren Anwendung" gibt. Je genauer Du sagen kannst, was für eine Anwendung das ist und welche Art von Daten übertragen und wie sie serialisiert werden, desto besser können wir helfen. Zudem verstehe ich immer noch nicht, warum Du unbedingt Vererbung benutzen und Deine Attribute dynamisch auflösen willst. Reichen da nicht einfache Kompositionen oder Mixins? Und: wenn die Anwendung ohnehin am Netzwerk lauscht und darüber kommuniziert, nutzt sie dann vielleicht schon ein fertiges Serialisierungsframework wie eines der von Rolf Magnus genannten, eins von PHP oder Perl oder etwas wie Boost::Serialization für C++? Dem schließt sich vor allem auch wegen des Hintergrundes Deines Beispielcodes auch die Frage an, wie Zeichenketten und andere Array-Typen übergeben werden; da gibt es ja eine Reihe von Möglichkeiten für die Spezifikation der Länge. Einige Serialisierungsmöglichkeiten, die ich kenne -- C, CSV und das interne Format von PostgreSQL zum Beispiel -- nutzen dort eine Art Feldtrenner, C zum Beispiel das abschließende Null-Byte, während andere die Länge als Prefix oder als Teil eines Formatstrings übergeben. Wie macht Dein "Server" das? > Python bietet extrem viele Möglichkeiten, aber mir fehlt ein bisschen > wie bei Qt das Metasystem, weil darüber kommt man relativ leicht an die > benötigten Information, aber anscheinend bietet Python hier auch schon > einiges. Gerade in der Metaprogrammierung ist Python sehr stark, weil im Grunde fast alles in Python -- Klassen, Methoden, Funktionen -- wiederum Python-Objekte sind. Im Anhang findest Du zwei Skripte: i.py zeigt ein bisschen Metaprogrammierung bei der dynamischen Erzeugung einer Klasse; das dort gezeigte benutze ich gerne im Zusammenhang mit dem Framework WTForms für HTML-Formulare. d.py dagegen zeigt fünf Python-Features, die dynamisch arbeiten: 1. Das "dataclasses"-Modul mit dem Decorator "@dataclass", der anhand der angegebenen Klassenattribute passende "magische" Methoden wie Konstruktor (_init_), Gleichheitsoperator (_eq_), Repräsentator (_repr_) etc. -- sofern die Klasse sie nicht bereits selbst definiert. Sehr spannend auch _hash_, das eine Klasse zu einem "Hashable" macht, womit sie zum Beispiel als Schlüssel in einem Dictionary (dict(), {}) genutzt werden kann. 2. Den Deskriptor "YearDesc", der eine Klassenvariable beschreibt (und in diesem Falle auch einschränkt). Deskriptoren können (normalerweise... auch da gibt es ein paar nifty Tricks) nur auf Klassenvariablen angewendet werden, wie in den Klassen "Student" und "Graduate". Sie "beschreiben" eine Variable, oder genauer die Operationen "__get__" und "__set__", die in meinem Beispielskript verwendet werden, um sicherzustellen, daß die zugewiesenen Werte wirklich Integers sind. 3. Die Funktion "validate_positive", die dem Konstruktor des Deskriptors YearDesc als Mitglied einer Liste von ausführbaren First Class Objects übergeben wird, um sicherzustellen, daß die übergebene Zahl positiv ist -- die Zahl könnte auch ein Float oder Complex oä. sein. 4. Eine Klasse mit der "magischen" Methode "__call__", deren Instanzen darum ausführbar sind, ähnlich wie bei functools.partial -- nur mit wesentlich mehr Kontrolle. Im Beispielskript benutze ich diese Klasse ebenfalls als Validator, welcher sicherstellt, daß der übergebene Wert sich im Bereich zwischen "mini" und "maxi" befindet. Bitte beachte, wie die Klasse in der __set__-Methode die ihrem Konstruktor übergebenen Validatoren aufruft. Das Skript "d.py" ist sicherlich eher was für fortgeschrittene Pythonistas, aber es zeigt ein paar etwas esoterischere Features, die für Deinen Anwendungsfall möglicherweise nützlich sind. Wenn Du Fragen hast, dann kannst Du natürlich gern hier im Forum fragen, aber wenn Du magst, freue ich mich auch über eine PN. Bitte beachte allerdings, daß ich meine privaten E-Mails bei Weitem nicht so häufig lese, wie ich das tun sollte... ;-)
Gast
#6678683
Erstmal Danke Sheeva für deine Hilfe. >Du schreibst weiter oben, daß es eine Gegenseite mir einer "nicht >veränderbaren Anwendung" gibt. Je genauer Du sagen kannst, was für eine >Anwendung das ist und welche Art von Daten übertragen und wie sie >serialisiert werden, desto besser können wir helfen. Zudem verstehe ich >immer noch nicht, warum Du unbedingt Vererbung benutzen und Deine >Attribute dynamisch auflösen willst. Reichen da nicht einfache >Kompositionen oder Mixins? Und: wenn die Anwendung ohnehin am Netzwerk >lauscht und darüber kommuniziert, nutzt sie dann vielleicht schon ein >fertiges Serialisierungsframework wie eines der von Rolf Magnus >genannten, eins von PHP oder Perl oder etwas wie Boost::Serialization >für C++? Ob ich den Namen der Applikation auf dem Server nennen kann weiss ich nicht, aber wie die Applikation genau die Daten serialisiert und deserialisert kann ich Dir nicht sagen, weil ich es selber nicht weiss, aber ist kein Protobuf, QDatastream oder Json. Du kannst Dir per Grafik die Daten zusammenstellen, welche als C/C++ Typen übertragen werden. Wie z.B. Temperatur, Tag, Monat, Jahr. Die Temperatur = int8 in Celsius, Tag = uint8, Monat = uint8 und Jahr uint16. Zusätzlich wird dem Daten eine feste Identifikation zugewiesen, damit Server und Client weiss um welche Daten es sich handelt. Welche Daten ausgetauscht werden ist dem Anwender überlassen. Die Daten werden mit Protokoll drumherum versendet oder empfangen. Zu dem Datenaustausch gibt es noch weitere Befehle, aber die sind hier uninteressant. Natürlich wird auch die Byteorder, Anfang der Nachricht und Länge in dem Protokol geachtet. Array-Typen hab ich gerade nicht im Kopf und wenn ich mich richtig erinnere werden Strings Null-terminiert. Wieso ich gerne Vererbung nutzen möchte liegt daran, dass es sich anbietet meiner Meinung nach anbietet. Ein technisches Beispiel: Ein Multimeter kann Gleichspannung, Wechselspannung, Gleichstrom und Wechselstrom messen. Ein Oszilloskop kann unter gewissen Umständen auch die Messungen tätigen und noch zusätzlich die Frequenz messen. Jetzt macht es für mich Sinn, dass das Oszilloskop die Eigenschaften und Methoden des Multimeters vererbt bekommt. Natürlich könnte ich auch zwei Klassen erstellen und beide Klassen besitzen identische Eigenschaften und Methoden, falls sich nun etwas ändert muss ich beide Klassen pflegen und nicht nur eine. Die Vererbung bietet sich in solchen Fällen an oder sehe ich das falsch? Der Dekorator @dataclass ist mir bekannt und auch Methodendekoratoren, welche ich für die Validierung nutzen würde. Hier ein Beispiel in Qt ohne Metainformationen und ohne Veerbung:
Eine Klasse mit zwei Q_PROPERTY, aber es können noch 60000k weitere Eigenschaften ausgetauscht werden, dann schreibst Du Dir hier ein Wolf um die Daten zu serialsieren.
Aus diesen Grund würde ich eher das Metasystem nutzen und über alle Eigenschaften iterieren die Datentype erkennen und in das Bytearray ablegen. Jetzt kommen wir auf das Problem im Eingangspost zurück. Ich muss jeder Klasse die Identifikation mitgeben, ansonsten weiss der Server oder Client nicht zu welcher Klasse die Daten gehören. Wenn ich nun alle Eigenschaften der Klasse und der Childklasse auslese mit [c] # hier bekomme ich alle Variablen a = vars(x) print(a) # Wie kann ich hier prüfen von welcher Klasse _firstname kommt? [\c] Dann weiss ich nicht aus welcher Klasse die Daten kommen. Also müsste ich den Objektbaum erhalten, dann bei jeder Klasse die Properties ermitteln, entsprechend der Datentype (uint8, int8, uint16, int16, ... ) in Bytes wandeln und die Identifikation anhängen. In dem Eingangsbeispiel hat die Person die Identifkation 1 und der Student die Identifikation 2. Serialsiere ich sturr die Variablen des Studenten in das Bytearray und hänge die Identifikation 2 an, dann wird die Applikation ein Fehler werfen, weil es zwei getrennte Datenpakete sind. Dirk schrieb: > Erstmal Danke Sheeva für deine Hilfe. Sehr gerne, dafür ist so ein Forum ja da... ;-) > Ob ich den Namen der Applikation auf dem Server nennen kann weiss ich > nicht, aber wie die Applikation genau die Daten serialisiert und > deserialisert kann ich Dir nicht sagen, weil ich es selber nicht weiss, > aber ist kein Protobuf, QDatastream oder Json. Du kannst Dir per Grafik > die Daten zusammenstellen, welche als C/C++ Typen übertragen werden. Wie > z.B. Temperatur, Tag, Monat, Jahr. Die Temperatur = int8 in Celsius, Tag > = uint8, Monat = uint8 und Jahr uint16. Zusätzlich wird dem Daten eine > feste Identifikation zugewiesen, damit Server und Client weiss um welche > Daten es sich handelt. Welche Daten ausgetauscht werden ist dem Anwender > überlassen. Die Daten werden mit Protokoll drumherum versendet oder > empfangen. Zu dem Datenaustausch gibt es noch weitere Befehle, aber die > sind hier uninteressant. Natürlich wird auch die Byteorder, Anfang der > Nachricht und Länge in dem Protokol geachtet. Array-Typen hab ich > gerade nicht im Kopf und wenn ich mich richtig erinnere werden Strings > Null-terminiert. Hm, mit den nullterminierten Strings könnte das mit struct.unpack() ein wenig schwierig werden, denn für dessen Formatstring mußt Du natürlich vorher wissen, wie lang der String ist. Das sollte aber machbar sein, wenn Du die Struktur einer Nachricht kennst und darüber iterieren kannst... am Ende sind bytes() und str() unter Python ja auch iterierbar. Cooler wäre es natürlich, wenn der Server etwas mitlieferte, aus dem Du Deinen Formatstring machen kannst... Aber nochmal zurück auf Anfang, nur um abzuklären, daß ich Deinen Anwendungsfall korrekt verstanden habe. Also, da gibt es eine Serveranwendung, der Du über ein Webinterface oder eine Clientsoftware ein Objekt schicken und sagen kannst "gib mir mal diese und jene Daten" -- also quasi so eine Art Datenbanksoftware. Dein Server sucht die Daten dann zusammen, packt sie in ein serialisiertes Format in Form eines Bytestrings, und die Clientsoftware, etwa eine Website klamüsert die serialisierten Daten nach dem Empfang wieder auseinander und... macht irgendwas damit, stellt sie also tabellarisch dar, visualisiert sie, speichert sie ab oder so etwas in dieser Art. Ist das grob richtig? ;-) > [siehe unten] > Ein technisches Beispiel: > > Ein Multimeter kann Gleichspannung, Wechselspannung, Gleichstrom und > Wechselstrom messen. Ein Oszilloskop kann unter gewissen Umständen auch > die Messungen tätigen und noch zusätzlich die Frequenz messen. Jetzt > macht es für mich Sinn, dass das Oszilloskop die Eigenschaften und > Methoden des Multimeters vererbt bekommt. Natürlich könnte ich auch zwei > Klassen erstellen und beide Klassen besitzen identische Eigenschaften > und Methoden, falls sich nun etwas ändert muss ich beide Klassen pflegen > und nicht nur eine. Die Vererbung bietet sich in solchen Fällen an oder > sehe ich das falsch? Ich würde da eher auf Komposition statt auf Vererbung gehen, aber nach meinem bisherigen Eindruck wäre es vermutlich sinnvoller, Deine Klassen dynamisch zu erzeugen. Vererbung und sogar eine Komposition bieten Dir an dieser Stelle keine realen Vorteile. Dafür handelst Du Dir aber einen wesentlichen Nachteil ein, denn Du verlierst dadurch an Flexibilität. Insofern würde ich -- wenn ich das alles richtig verstanden habe, natürlich -- nur für das "äußere" Format, also quasi den gleichbleibenden Briefumschlag der Nachrichten (wie zum Beispiel die von Dir genannte Identifikation) die Vererbung einsetzen. Im Anhang findest Du mit "e.py" ein kleines Skript, daß die Datenklassen als @dataclass dynamisch erzeugt. Das hat ein paar kleine Vorteile: zum Einen hast Du dann tatsächlich eine Art "Metasystem", aus dem Du Informationen abfragen kannst, wie die Methode BaseData.getField() zeigt. Zum Anderen kannst Du die festen Teile Deiner Servernachrichten in einer Basisklasse (hier: BaseData) anlegen und Deine dynamischen Daten dann relativ einfach hinzufügen. Tatsächlich kannst Du dafür sogar Formatstrings für __repr__() und Deine Serialisierung / Deserialisierung mithilfe von struct.[un]pack() dynamisch zusammenbauen. Ich würd mir vielleicht ein enum.Enum dafür bauen, dessen Werte jeweils ein collections.namedtuple() mit den Eigenschaften "format_pack", "format_unpack", und "field" für den passenden Deskriptor wären... oder vielleicht auch ein dict() statt eines Enum. Damit kann ich dann über die Felder der erwarteten Nachricht iterieren und mir sowohl die Formatstrings für die (De-)Serialisierung als auch die Deskriptoren ziehen, und daraus dann die zu erwartende Nachrichtenklasse dynamisch aufbauen. Vielleicht würde ich auch noch ein Feld "methods" in mein namedtuple() mit Methoden hinein bauen, die mein dynamisch gebaute Klasse am Ende erhalten soll. Tipp am Rande: mit type() können ebenfalls Klassen dynamisch erzeugt werden, das ist meines Wissens der Mechanismus, den dataclasses.make_dataclass() benutzt. > Der Dekorator @dataclass ist mir bekannt und auch Methodendekoratoren, > welche ich für die Validierung nutzen würde. Naja, in meinem Beispiel d.py benutze ich keine Methodendekoratoren, sondern Deskriptoren für die Validierung. Das erscheint mir ein bisschen flexibler zu sein als zum Beispiel der Standard-Dekorator @property, der intern übrigens seinerseits ebenjene Deskriptoren benutzt... ;-) > Aus diesen Grund würde ich eher das Metasystem nutzen und über alle > Eigenschaften iterieren die Datentype erkennen und in das Bytearray > ablegen. Najaaa... also so ein Metasystem wie jenes von Qt gibt es in Python meines Wissens nicht, aber ich kenne mich mit Qt nicht mehr sonderlich gut aus -- zuletzt habe ich mal eine GUI-Applikation mit PerlQt entwickelt, allerdings dürfte das jetzt auch schon wieder lockere fünfzehn Jahre her sein... Also sollten wir lieber davon ausgehen, daß ich von Qt keine Ahnung habe. ;-) > [c] > # hier bekomme ich alle Variablen > a = vars(x) > print(a) > # Wie kann ich hier prüfen von welcher Klasse _firstname kommt? > [\c] Naja... im Grunde genommen kannst Du Dich anhand der Method Resolution Order (mro, wie oben bereits erwähnt) an den Parent-Klassen entlang hangeln und für jede davon schauen, ob Deine Eigenschaft dort vorhanden ist. Die letzte Klasse, in welcher Deine Eigenschaft vorhanden ist, ist dann jene, die sie enthält. Und, nebenbei: mit vars() bekommst Du NICHT alle Variablen, sondern nur die in <instanz>.__dict__ -- die in <instanz>.__slots__ und <klasse>.__slots__ nicht. Wirklich alle Variablen, auch die "privaten" (oder besser: nach der Konvention "versteckten") bekommst Du mit
-- und natürlich kannst Du da dann wie in jeder Comprehension auch filtern, was genau Du haben möchtest. Das ist deswegen wichtig, weil zum Beispiel bei diesen @dataclass-Klassen ein ganzer Haufen der Magie sich in versteckten Attributen wie _dataclass_fields_ abspielt und Du die dort enthaltenen Informationen quasi als Metasystem benutzen kannst, ohne die Eigenschaften Deiner Klasse zuvor aufwändig auszeichnen und / oder hinterher aufwändig filtern zu müssen. HTH, YMMV... ;-)
Gast
#6682736
>Hm, mit den nullterminierten Strings könnte das mit struct.unpack() ein >wenig schwierig werden, denn für dessen Formatstring mußt Du natürlich >vorher wissen, wie lang der String ist. Das sollte aber machbar sein, >wenn Du die Struktur einer Nachricht kennst und darüber iterieren >kannst... am Ende sind bytes() und str() unter Python ja auch >iterierbar. Cooler wäre es natürlich, wenn der Server etwas mitlieferte, >aus dem Du Deinen Formatstring machen kannst... Die Information der Länge von Strings ist im Protokol behandelt, somit kein Problem. >Aber nochmal zurück auf Anfang, nur um abzuklären, daß ich Deinen >Anwendungsfall korrekt verstanden habe. Also, da gibt es eine >Serveranwendung, der Du über ein Webinterface oder eine Clientsoftware >ein Objekt schicken und sagen kannst "gib mir mal diese und jene Daten" >-- also quasi so eine Art Datenbanksoftware. Dein Server sucht die Daten >dann zusammen, packt sie in ein serialisiertes Format in Form eines >Bytestrings, und die Clientsoftware, etwa eine Website klamüsert die >serialisierten Daten nach dem Empfang wieder auseinander und... macht >irgendwas damit, stellt sie also tabellarisch dar, visualisiert sie, >speichert sie ab oder so etwas in dieser Art. Ist das grob richtig? ;-) Fast, es ist keine Webapplikation mit Restinterface, sondern eine Applikation, welche per TCP gesteuert und Daten ausgetauscht wird wwischen dem Server und den Client. Mit deinen Beispielen bin ich jetzt auch ein Schritt weitergekommen. Ich hab erstmal auf die Vererbung verzichtet und bin wie empfohlen über Komposition gegangen. Jetzt gefällt mir das alles schon viel besser und spart viel Arbeit, wenn Variablen in der Klasse dazukommen oder wegfallen. Vorher war es echt harte Fleissarbeit die Klassen, Serialisierung und Deserialsierung anzupassen. Jetzt muss ich nur die Klasse anpassen und der Rest passiert dynamisch :) Besten Dank für die Hilfe! Dirk schrieb: > Mit deinen Beispielen bin ich jetzt auch ein Schritt weitergekommen. Ich > hab erstmal auf die Vererbung verzichtet und bin wie empfohlen über > Komposition gegangen. Jetzt gefällt mir das alles schon viel besser und > spart viel Arbeit, wenn Variablen in der Klasse dazukommen oder > wegfallen. Vorher war es echt harte Fleissarbeit die Klassen, > Serialisierung und Deserialsierung anzupassen. Jetzt muss ich nur die > Klasse anpassen und der Rest passiert dynamisch :) Cool, das freut mich sehr, daß ich Dir helfen konnte. > Besten Dank für die Hilfe! Sehr gerne, und Dir vielen Dank für die Rückmeldung! ;-) Antwort schreibenBitte melde dich an, um einen Beitrag zu schreiben. |
Anzeige
|