SimpleXMLRPCServer recursive dictionaries

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Habe folgendes Problem, ist nicht wirklich Netzwerkspezifisch, deshalb poste ich es hier, wenn jemand anderer Meinung ist, bitte verschieben.

Ich nutze ein SimpleXMLRPCServer um schnell Instanzen zu übertragen,
nun haben ich Bildlich eine solche Strucktur:

Code: Alles auswählen

class a(object):
    def __init__(self):
         self.bs = {}
         ...

class b(object):
    def __init__(self):
         self.ref = "None"
         ...

instance_a = a()
instance_b = b()

instance_a.bs["0"] = instance_b
instance_b.ref = instance_a
instance_b braucht die referenz auf eine Instanz von a, da der XMLRPCServer Dictionarys aus den Instanzen macht, führt dies logischerweise zu einer recursiven Dictionary.

Wie könnte ich der Rekursion entgehen ?

Danke, schonmal für jede Hilfe.
Zuletzt geändert von Xynon1 am Donnerstag 25. November 2010, 12:49, insgesamt 2-mal geändert.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Deine Klassenmodell neu strukturieren und die Abhängigkeiten beseitigen. Gegenseitige Abhängigkeit von Klassen ist meist ein Zeichen für ein schlechtes Design.
„Lieber von den Richtigen kritisiert als von den Falschen gelobt werden.“
Gerhard Kocher

http://ms4py.org/
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Die Klassen sind ja nicht direkt von einander Abhängig, sondern nur diese einzelne Instanz.
Ok, so weit mit dem umdesign war ich auch schon, sonst hätte ich das ja in Netzwerkprogrammierung untergebracht und nicht hier.

Jetzt stellt sich mir aber die Frage, wie ?

Also um das Problem zu verdeutlichen konkretisiere ich mein Beispiel oben, man stelle sich vor, es gibt Fahrzeughalter, und Fahrzeuge.

Also etwas in der Art:

Code: Alles auswählen

class Fahrzeughalter(object):
    def __init__(self):
        self.fahrzeuge = []
        ...
    def neues_fahrzeug_kaufen(self, fahrzeug):
        fahrzeug.besitzer = self
        self.fahrzeuge.append(fahrzeug)

class Fahrzeug(object):
    def __init__(self):
        self.besitzer = None
        ...

person_a = Fahrzeughalter()
person_b = Fahrzeughalter()

auto_a = Fahrzeug()
person_a.neues_fahrzeug_kaufen(auto_a)
person_b.neues_fahrzeug_kaufen(Fahrzeug())
person_b.neues_fahrzeug_kaufen(Fahrzeug())
Also jeder Fahrzeughalter, kann mehrere Fahrzeuge besitzen, richtig ?
Aber jedes Fahrzeug gehört nur einem Halter.

Wenn nicht so wie kann man dennoch die Relationen aufrecht erhalt, so das sich jede Entität selbst verwalten kann, und trotzallem eine Verbindung besteht ?
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Meine Idee wäre nur die ID, also die eineindeutige Eigenschaft des Fahrzeughalters beim Fahrzeug zu speichern.

Eine Liste mit allen Fahrzeughaltern hätte ich schon, nur dann müsste ich jedesmal diese durchgehen und den Fahrzeughalter suchen, was imho auch nicht schön ist, weil das sehr viele werden können.
Achso und das hier ist alles nur ein Beispiel und dient der Vereinfachung in meinem Programm geht es um etwas anderes.
Deshalb müsste man dazusagen für dieses Beispiel, es werden sehr viele Fahrzeuge gekauft und deshalb würde eine Liste stark ausbremsen.

Andere Ideen oder Anregungen ?
Falls noch Informationen erforderlich sein sollen, müsst ihr was sagen.
oder schreckt der Titlename dermaßen ab ? :mrgreen:
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
BlackJack

@Xynon1: Zusätzlich zur Liste ein Dictionary das ID auf Element abbildet geht nicht?
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Stimmt hast recht, das da wären die Zugriffszeit zumindest ähnlich,
allerdings müsste ich alle IDs, dann immer in Strings umwandeln, da beim senden von Dictionaries der SimpleXMLRPCServer kein Integer Keys zulässt.

Müsste mal ein paar Geschwindigkeitstests machen, aber dennoch fände ich es schöner, wenn man die Referenz auf die Instanz speichern könnte.
Aber eventuell kann man da nichst machen. :?

Achso @ms4py, wieso ist dies "ein Zeichen für schlechtes Design" ?
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Ein paar Umwandlungen und eine Indirektstufe mehr machen nicht wirklich viel aus, so dass man sich um Geschwindigkeit keine Sorgen machen braucht. Von wie vielen Daten sprichst du denn? Und jetzt komm nicht mit Zahlen < 10^6 ;-)
Das Leben ist wie ein Tennisball.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

mh, prinzipiell etwas mehr --> ∞ :mrgreen:

Nein, Spaß beiseite,
Theoretisch müssen alle Listen/Dictionaries alle 50 max. 500 ms abgetastet werden,
die Daten liegen ungefähr bei 1000 bis 5000 Fahrzeughaltern und einer Steigenden Zahl von Fahrzeugen, zurechnen, wäre dort bis 50000, vieleicht auch etwas mehr.
^--- Immernoch auf das Beispiel bezogen

Meine Sorge liegt nur im Zeitfenster, welches ich möglichst klein halten möchte.
Denn das Suchen und Finden, ist ja nicht das einzige was ich mache :wink:
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Die Frage ist dann, welche Aktionen Du von dem geschilderten Szenario zeitkritisch aufführen möchtest. dictionary lookups sind O(1), besser kannst Du die Suche quasi nicht hinbekommen. Für eine kleine Anzahl von Fahrzeughaltern reicht vermutlich der Listenansatz, da die Erstellung des dictionary evtl. mehr Zeit kostet als es Dir bei der Suche einspart (und 5000 sind wirklich nicht viel).

Ebenso könntest Du auf Senderseite die ID des Fahrzeughalters einfach als Instanzvariable dem Auto geben und auf Empfängerseite dann die nötige Zugriffstruktur erstellen. Das dictionary müsste so nicht übertragen werden, da die Information in den Objekten steckt.

Musst Du mit jeder Anfrage alle Objekte von A nach B schaufeln? Oder werden die in B vorgehalten? Oder nur Hinzukommende? Welche Vorgehensweise sinnvoll ist, hängt hiervon ab und lässt sich auf insert vs. search Optimierung reduzieren, und der passende Datentyp ergibt sich hieraus.

Edit:
Hätt's fast vergessen, doppelte Datenhaltung ist *puh. Nichts weiter ist nämlich Deine Rückreferenz hier. Was auch ginge:

Code: Alles auswählen

class Halter:
    def __init__(self, autos):
        self.autos = autos

class Auto:
    def __init__(self):
        # keine Referenz auf Halter

halter = [Halter((Auto(), ...)) for i in range(3)]
Jetzt musst Du nur die Liste 'halter' übertragen. Dort sind alle Autos mit drin. Ein Abbildung von Auto auf Halter kannst Du leicht hieraus erstellen (auf Empfängerseite z.B., und wenn Du es brauchst, an das Auto-Objekt hängen). Und für Autos ohne Halter kannst Du ja ein Halter-Exemplar "reservieren".
Zuletzt geändert von jerch am Donnerstag 25. November 2010, 16:56, insgesamt 3-mal geändert.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Ok, stetig in den Dictionraries rumkurven geht, ist aber nicht sehr schön.

@jerch
Aber Mir gefällt deine Idee ganz gut, einfach die Referenz behalten und nur die ID übertragen,
du weißt nicht zufällig noch wie ich das bewerkstellige ?

Dazu gleich die Information.
jerch hat geschrieben:Musst Du mit jeder Anfrage alle Objekte von A nach B schaufeln? Oder werden die in B vorgehalten? Oder nur Hinzukommende? Welche Vorgehensweise sinnvoll ist, hängt hiervon ab und lässt sich auf insert vs. search Optimierung reduzieren, und der passende Datentyp ergibt sich hieraus.
Nein, muss ich nicht, der Client kann die Information die er benötigt, über bereitgestellte Funktionen übertragen lassen.
Der Server verwaltet alles.

Also, ich würde es gerne nun wie folgt machen:

Code: Alles auswählen

class Fahrzeughalter(object):
    def __init__(self):
        self.fahrzeuge = []
        ...

class Fahrzeug(object):
    def __init__(self):
        self._besitzer = None
        self.besitzer_id = 0
        ...
Problem ist nur, das der Server alle Attribute der Instanz serilisiert, aber ich würde es nun gerne so gestalten das es nur bei Attributen geht die nicht mit "_" anfangen.

Geht das ?

Also ich habe die @expose Möglichkeit gesehen, aber die geht nur bei Methoden.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@Xynon1:
Hatte oben nochwas editiert, während Dein neuer Beitrag kam. Vllt. ist ja eine solche Vereinfachung mgl.
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

jerch hat ja schon beschrieben, was daran schlecht ist. Das ist einfach unnötige Redundanz...
„Lieber von den Richtigen kritisiert als von den Falschen gelobt werden.“
Gerhard Kocher

http://ms4py.org/
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Das Problem nur daran ist,
wie weiß das Auto jetzt zu wem es gehört ? (intelligentes auto :) )

Da das Auto zum Beispiel, ist jetzt vieleicht kein gut gewähltest Beispiel, aber sagen wir mal das Auto will zu seinem Besitzer Fahren.
Also sag ich ihm auto.fahr_zu_besitzer(), nun muss das Auto wissen, wo sein Besitzer sich aufhält, der Besitzer kennt seinen Standort.
Nur der Besitzer ist aber nirgendwo global definiert.

Was ich damit sagen will, der Besitzer hat einige Methoden, welche das Auto benötigt.
Ok, ist wirklich ein dämliches Beispiel, aber das Auto braucht auch die Daten vom Besitzer.


@ms4py
Richtig, für den Client ist dies eigentlich irrellevant, der braucht wirklich nur die ID des Besitzers, aber der Server braucht die Daten.
Da wie schon erwähnt einige Methoden daran hängen und ich keine Globale Variable habe welche ich für die Verwaltung aller Besitzer verantwortlich ist.

Und imho ist das nur redundant, wenn es in eine Dictionary umgewandelt wird, ansonsten speicher ich nur die Referenz um die 1:n Beziehung zuhaben, oder sehe ich das gänzlich falsch ?
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

In einem relationalen Modell würde die Datenstruktur mit 1:N Beziehung so aussehen:

Code: Alles auswählen

Tabelle Besitzer
Id (PK)| Name | ...

Tabelle Fahrzeug
Id (PK) | BesitzerId (FK Besitzer.Id) | Marke | ...
Dein Modell lässt sich relational mit atomaren Elementen gar nicht abbilden, das ist schon ein deutliches Zeichen, dass es sich um eine ungünstig gewählte Datenstruktur handelt.

Edit: Vielleicht kommt es ja sogar in Frage, die Daten auf Serverseite relational in einer DB abzulegen. Dein Beispiel scheint wie gemacht dafür zu sein ;)

Edit2: Evt. könnte auch Pyro was für dich sein, um Objekte deutlich eleganter über das Netz auszutauschen: http://www.xs4all.nl/~irmen/pyro3/
„Lieber von den Richtigen kritisiert als von den Falschen gelobt werden.“
Gerhard Kocher

http://ms4py.org/
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

ms4py hat geschrieben:In einem relationalen Modell würde die Datenstruktur mit 1:N Beziehung so aussehen:

Code: Alles auswählen

Tabelle Besitzer
Id (PK)| Name | ...

Tabelle Fahrzeug
Id (PK) | BesitzerId (FK Besitzer.Id) | Marke | ...
1. Stimmt, weil hier der Besitzer selbst nicht weiß, welche Autos er besitzt.

2. Nein, lohnt sich nicht, da die Daten auf der Serverseite temporärer Natur sind.

3. Werde mich gleich mal genauer in pyro einlesen, doch die ersten zwei Seiten davon, deuten stark darauf hin, das das Prizip sich ziemllich ähnelt, nur sieht es etwas ausgereifter aus.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Xynon1 hat geschrieben:Und imho ist das nur redundant, wenn es in eine Dictionary umgewandelt wird, ansonsten speicher ich nur die Referenz um die 1:n Beziehung zuhaben, oder sehe ich das gänzlich falsch ?
Das zusätzliche Vorhalten der Referenz ist redundant. Das wird Dir schnell klar, wenn Du Dir überlegst, welche Daten Du korrigieren musst, falls ein Auto den Halter wechselt. Solche Konstrukte sind fehleranfällig, da Du zur Implementationszeit sicherstellen musst, dass die Daten konsistent bleiben.
Xynon1 hat geschrieben:wie weiß das Auto jetzt zu wem es gehört ? (intelligentes auto :) )
Gönne dem Auto einen Blick auf den Besitzstatus, in meinem obigen Bsp. auf die "globale" Halterliste, z.B. so:

Code: Alles auswählen

# Klasse Auto
...
    def who_owns_me(self, halter):
        for h in halter:
            for a in h.autos:
                if a == self:
                    return a
        return Halter() # default "None" Halter
Brauchst Du viele solche Zugriffe, kann es sinnvoll sein, ein dictionary zu erstellen:

Code: Alles auswählen

auto_zu_halter = dict((a, h) for h in halter for a in h.autos)
Hier hast Du allerdings wieder das Redundanzproblem und musst mit jeder Änderung das dictionary aktualisieren.

Und weil Du auf eine Rückreferenz bestehst ;) :

Code: Alles auswählen

# auf empfängerseite
def set_backrefs(halter):
    for h in halter:
        for a in h.autos:
            a.halter = h
NB:
Ich weiss nicht, wie komplex Deine echten Objekte sind und ob Du nicht mit einer Übertragung der "reinen Daten" und Erstellen der Objekte auf Empfängerseite besser fährst. Auch würde hiermit der nötige Datenaustausch geringer. Sowas ginge z.B. mit einer klasseneigenen serialize()-Methode auf Senderseite und entsprechendem Konstruktor auf Empfängerseite, der aus den serialisierten Daten das Objekt restaurieren kann.
Das Übertragen von eigenen Objekten (also jenseits von standardisierten Datencontainern) macht eigentlich nur Sinn, wenn der Empfänger nicht deren Deklaration kennen kann (z.B. beim Einsatz von Fabrikmethoden u.ä.) oder die Serialisierung zuviel Aufwand bedeuten würde. Pyro wurde ja schon genannt.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Den oberen Teil, hättest du dir sparen können, dennoch danke für die Veranschaulichung und deine Mühen, so was weiß ich immer zu schätzen :D

Ja, stimmt, der Wechsel ist nicht vorgesehen, brauch ich auch nicht, aber das schildert deutlich das Problem.

Die auto_zu_halter Dictionary ist glaube ich noch weniger zuträglich, und wie schon gesagt ich habe weder eine globale Autoliste noch eine globale Halterliste.

Die Struckturen sind ziemlich groß, dies ist nur eine wo ich nicht mehr weiter wusste und da bot sich das an.
Und wie man sieht, nicht zu meinen Gunsten.

Ach ja, die Objekte müssen nicht restauriert werden, der Client braucht nur die Daten.

Also, ich denke ich werde einfach immer die einzelne Dictionary durch laufen, das erscheint mir im Moment am einfachsten und ging/geht ja auch schon. Auch zeitlich.
Nur wäre es jetzt noch schön wenn ich von der globalen "Halterliste" wegkomme.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
BlackJack

@alle die hier die Redundanz ansprechen: Das mag ein Code-Smell sein, ist aber nicht zwangsläufig ein schlechter Entwurf. Man stelle sich zum Beispiel Daten vor, die letztendlich einen gerichteten Graphen beschreiben, bei dem auch Kreise, bis hin zu solchen mit zwei oder gar nur einem Knoten, vorkommen können. Selbst bei einer Baumstruktur können Rückverweise auf den Elternknoten oder sogar eine zusätzliche Verkettung aller Knoten je Ebene, super praktisch sein. Das mag mehr Aufwand bei der Modifikation bedeuten, aber die gibt es ja nicht immer oder die Vorteile der einfachen Navigation wiegen das wieder auf.
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Oder er macht halt doch das Ganze relational. Sqlite ist ja mit Python dabei und kann auch eine DB im RAM anlegen. Und dann existiert eine globale Halter- und Fahrzeugliste und man hat auch keine Performance-Probleme.

Die Daten musst du dann immer noch vernünftig an den Client übertragen, aber du hast dann keine Probleme mehr mit der Navigierbarkeit in beide Richtungen, du nimmst dann einfach nur die, die du brauchst)
„Lieber von den Richtigen kritisiert als von den Falschen gelobt werden.“
Gerhard Kocher

http://ms4py.org/
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Das ist definitiv auch eine Option, ich werde mir mal ansehen, wie komplex mein Server noch wird, eventuell lohnt es sich.
Momentan brauch ich das nämlich nur an einer Stelle.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Antworten