Objekt/KLasse: Methoden/Member herausfinden und aufrufen

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.
am2
User
Beiträge: 17
Registriert: Montag 8. September 2014, 09:57

Keine Ahnung, wie ich das beschreiben soll.
Ich bekomme von einem WebService ein Objekt angeliefert. Mittels __dir__(MyObjekt) bekomme ich heraus, welche Member das Objekt hat

Code: Alles auswählen

from suds.client import Client
MyURL = 'http://192.168.0.1:80/BlaBla?wsdl'
MyClient = Client(MyUrl)
MyResult = MyClient.service.CalledFunction(MyParams)
print dir(MyResult)
Jetzt kann ich nachlesen, dass es beispielsweise eine Methode oder eine Membervariable mit dem Namen "MyMember" gibt.

Ich kann danach auch

Code: Alles auswählen

MyResult.MyMember = 15
schreiben

Nun meine Fragen:
1. Wie kann ich die Methoden über den Namen ansprechen? (irgendwas, wie

Code: Alles auswählen

MyVar="MyMember";MyResult.<MyVar> = 15
?)
2. (Wie) kann ich unterscheiden zwischen Membervariablen und Methoden?

Vielen Dank
Andreas
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Irgendwie gleichen sich die Fragen heute alle irgendwie... :P
am2 hat geschrieben:1. Wie kann ich die Methoden über den Namen ansprechen?
Über die `getattr()` Funktion.
am2 hat geschrieben:2. (Wie) kann ich unterscheiden zwischen Membervariablen und Methoden?
Über die `callable` Funktion.

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
am2
User
Beiträge: 17
Registriert: Montag 8. September 2014, 09:57

Danke, genau das habe ich gesucht.
am2
User
Beiträge: 17
Registriert: Montag 8. September 2014, 09:57

Und da ist er wieder, der Hä- Effekt:

Ich möchte aus einem Python- Objekt ein Dictionary machen, da es an einen WebService übergeben werden soll, der anscheinend nur mit Dictionarys zusammenarbeitet.
Das heißt, es sollen alle Member- Variablen in ein Dictionary geworfen werden:

Code: Alles auswählen

def Member2Dict(MyObject):
    lvReturnDict = {}
    for lvsMember in dir(MyObject):
        print "Member", lvsMember
        if not callable(getattr(MyObject,lvsMember)):
            lvReturnDict[lvsMember] = getattr(MyObject,lvsMember)
    print lvReturnDict
Das Ganze geht aber schief dahingehend, dass er zwar anscheinend alle Member ins Dictionary einträgt - die Schleife läuft durch - aber das Hinschreiben "print lvReturnDict" mit einer Fehlermeldung "AttributeError: Metadata instance has no attribute '__metadata__'" abgebrochen wird ("__metadata__:" steht noch da). Ich könnte es mir leicht machen und alles ignorieren, was der Maske "__*__" genügt, aber ich will ja eigentlich auch verstehen, was da passiert. Vorstellen könnt ich mir, dass das "Ding", was an __metadata__ dranhängt, nicht ausgeschrieben werden kann (keine __str__- Fuinktion?). Ist das so?`

ps: Die prints sind natürlich Spaß, final wird lvReturnDict zurückgeworfen und gut soll sein.

Danke
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@am2
Wenn Du die Attribute einer Instanz möchtest, dann verwende doch `__dict__`:

Code: Alles auswählen

class AnyClass(object):
    def __init__(self, any_attribute, another_attribute):
        self.any_attribute = any_attribute
        self.another_attribute = another_attribute
   ...:         

In [2]: ac = AnyClass('foo', 'bar')

In [3]: ac.__dict__
Out[3]: {'another_attribute': 'bar', 'any_attribute': 'foo'}
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
am2
User
Beiträge: 17
Registriert: Montag 8. September 2014, 09:57

Hatte ich vorher probiert, bekam aber eine Fehlermeldung.
Jetzt, wo ich es nochmal probiert habe, stelle ich fest, dass es exakt die gleiche Fehlermeldung an der gleichen Stelle ist.

Was also tun? Im meiner MakeDict- Routine kann ich zumindest aktiv die "bösen" Attribute auslassen oder in einen try- Block prügeln, bei __dict__ laufe ich vermutlich unweigerlich in den Fehler.
Wie kann ich den Fehler an sich umgehen? Wenn ich direkt in der Schleife versuche, alles gleich zu printen, dann kommt genau bei

Code: Alles auswählen

print getattr(MyObject,lvsMember)
die Fehlermeldung, wenn lvsMember = "__metadata__"

Außer ignorieren bleibt mir nicht viel, oder?
BlackJack

@am2: Zur Unterscheidung Datenattribute und Methoden reicht `callable()` nicht aus denn man kann ja ein aufrufbares Objekt auch als Datenattribut haben.

Warum kannst Du nicht einfach die Namen der Attribute angeben die als Wörterbuch herausgezogen werden? Das ist wesentlich sicherer als zu versuchen zu entscheiden was man nun als Datum und was als Methode ansieht. Die API die Du dort ansprichst wird doch irgendas bestimmtes erwarten.

Auf der anderen Seite gibt es vielleicht einen spezifischeren Weg denn `__metadata__` klingt irgendwie nach einem ORM oder ähnlicher ”Magie”.

Die Namensgebung ist übrigens gruselig. Unkonventionelle Gross-/Kleinschreibung, konkrete Datentypen in den Namen *und* noch etwas das nach falsch verstandener ungarischer Notation als Präfix aussieht, plus Begriffe die in Python nicht üblich sind (Member) und die Mutter aller unnützen Präfixe „My”.
am2
User
Beiträge: 17
Registriert: Montag 8. September 2014, 09:57

Warum kannst Du nicht einfach die Namen der Attribute angeben die als Wörterbuch herausgezogen werden? Das ist wesentlich sicherer als zu versuchen zu entscheiden was man nun als Datum und was als Methode ansieht. Die API die Du dort ansprichst wird doch irgendas bestimmtes erwarten.
Ich möchte das gern so universell halten, wie möglich. Im Lauf der Zeit werde ich mit allen möglichen WS auf der einen Seite und Objektklassen auf der anderen Seite hantieren müssen. Ich habe schlicht keine Lust, dann wie ein Rechtschreibprogramm alle möglichen Schreibweisen aus XML- Dokumenten abschreiben zu müssen. Bestimmte Dinge muss ich einfach nur durchtransportieren, ohne sie anzusehen.
Auf der anderen Seite gibt es vielleicht einen spezifischeren Weg denn `__metadata__` klingt irgendwie nach einem ORM oder ähnlicher ”Magie”.

Die Namensgebung ist übrigens gruselig. Unkonventionelle Gross-/Kleinschreibung, konkrete Datentypen in den Namen *und* noch etwas das nach falsch verstandener ungarischer Notation als Präfix aussieht, plus Begriffe die in Python nicht üblich sind (Member) und die Mutter aller unnützen Präfixe „My”.
Ja, die Namensgebung ist ein Kompromiss zwischen dem, was ich für weniger gruselig halte, und dem, was hier üblich ist. Das Präfix "My" ist nur, um einen definitiv freien Bezeichner zu erzwingen (mach ich selbstverständlich sonst nicht), Anfangsbuchstaben von Wörtern gehören für mich IMMER groß, sonst liest sich das für mich beschissen, die "falsch verstandene ungarische Notation" ist eine, die ich mir in den letzten Jahren als praktikabel erarbeitet habe. Ich weiß, dass die Schlangenbändiger das nicht mögen, weil sie "ein Relikt" aus Programmiersprachen mit fester Typisierung darstellt.
Ich mag Typisierung und gerade bei python habe ich festgestellt, dass ich irre werde, wenn ich nicht weiß, ob ich ein array, ein array von arrays, ein dict, eine Zeichenkette oder was auch immer am Wickel habe. Ich kann dann einfach wirklich nicht mehr lesen, was dort passieren soll. Ich programmiere seit fast 30 Jahren, so schnell stelle ich mich nicht um, nur weil ein neuer Modetrend aufgekommen ist. Der geht auch wieder ...
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@am2: was für die einen gruselig ist, ist für die anderen schön und umgekehrt. Du solltest wissen, was in einer Variable steht (inhaltlich und nicht vom Typ her), dann weißt Du auch, ob das etwas list-ähnliches oder dict-ähnliches ist. Wenn Du nur nach dem Präfix gehst, dann schreibst Du Code, von dem Du nicht weißt, was er macht.
Zum Problem: Warum willst Du das WebService-Objekt in etwas anderes konvertieren? Um es herumzureichen braucht es ja kein Dictionary zu sein. Diesen unnötigen Konvertierungsschritt kannst Du Dir auch sparen.
am2
User
Beiträge: 17
Registriert: Montag 8. September 2014, 09:57

Der Inhalt geht ja aus dem Namen der Variable hervor ;)

Im konkreten Fall habe ich Daten in einer DB, diese hole ich mittels python heraus, arbeite sie auf, werfe sie einem Web- Service zum Fraß vor, sammle das Ergebnis wieder ein, arbeite es auf und schubse es in die Datenbank zurück.
Es handelt sich im Grunde IMMER um das gleiche "Ding", aber in verschiedenen Darstellungsformen (und angereichert mit einer unterschiedlichen Menge von Informationen). Um dabei den Überblick zu behalten, muss ich auch wissen, welche Inkarnation ich gerade in der Hand habe.

Ursprünglich wollte ich dem Web- Service ja das Objekt zum Fraß vorwerfen, aber - was immer ich falsch gemacht habe - das ging nicht rüber.

Code: Alles auswählen

from suds.client import Client
globalUrl = 'http:192.168.0.1:80/AnyService'
globalClient = Client(globalUrl)

class Test(object):

a = Test()
a.x = 10
a.y = 10
 
MyResult = globalClient.service.MyFunction(a)
Beim Webservice kommt nix an, warum auch immer. Vermutlich übergibt er nur die Adressen. MyFunction({x=10:y=10}) funktioniert hingegen. Daher dieser Konvertierungswahn.

Vielleicht habe ich aber auch nur die falsche Frage gestellt, die eigentliche Frage hätte dann lauten müssen: Wie übergebe ich ein komplettes Objekt an einen WebService?
BlackJack

@am2: Da wäre die Antwort wohl „gar nicht”, denn man kann da ja nicht einfach beliebige Objekte übergeben. SOAP kennt ja überhaupt keine Python-Objekte. Mir ist auch immer noch nicht klar wie Du denkst das das auf magische Weise funktionieren soll. Denn selbst wenn Du die Attribute alle von dem Objekt abfragst,aufrufbare ausfilterst, und in ein Wörterbuch steckst, dann können die Werte ja immer noch bzw. wieder beliebige Python-Objekte sein, also wieder etwas was die SOAP-Bibliothek nicht kennt und deshalb auch nicht als XML serialisieren kann. Und nochmal: Die Aufrufe werden doch irgendeine API bedienen also muss man auch genau das übergeben mit genau den Argumenten und Namen die dort gefordert sind. Und nicht ein irgendwie in ein Wörterbuch verfrachtetes Python-Objekt in der vagen Hoffnung, dass das schon irgendwie funktionieren wird. ”Adressen” können nicht übergeben werden, die gelten nur innerhalb eines Prozesses, und ausserdem ist das vom Konzept her unterhalb der Ebene auf der man in Python mit Objekten arbeitet.

Du zeigst hier syntaktisch falschen Python-Quelltext und irgendwelche allgemeinen Objekte und SOAP-Methoden. Zeig doch mal bitte einen echten Fall, ohne ”Magie” also alles schön ausgeschrieben. Bevor man etwas vereinfacht und automatisiert muss man ja erst einmal das Problem erfassen was man lösen muss, und das habe ich bisher nicht verstanden beziehungsweise habe ich den starken Eindruck Du auch nicht.
am2
User
Beiträge: 17
Registriert: Montag 8. September 2014, 09:57

@am2: Da wäre die Antwort wohl „gar nicht”, denn man kann da ja nicht einfach beliebige Objekte übergeben. SOAP kennt ja überhaupt keine Python-Objekte. Mir ist auch immer noch nicht klar wie Du denkst das das auf magische Weise funktionieren soll. Denn selbst wenn Du die Attribute alle von dem Objekt abfragst,aufrufbare ausfilterst, und in ein Wörterbuch steckst, dann können die Werte ja immer noch bzw. wieder beliebige Python-Objekte sein, also wieder etwas was die SOAP-Bibliothek nicht kennt und deshalb auch nicht als XML serialisieren kann.
Vermutlich erwarte ich zu viel. Aber im Grunde wird ein XML geschickt. Ich hatte bislang keine Lust, mir die suds- Bibliotheken anzusehen, aber für mich war es schon vorstellbar, dass eine solche Bibliothek vorhandene (python) Objekte nimmt und daraus ein "äquivalentes" XML macht. Umgekehrt kommt ja auch ein XML zurück, was klaglos in python- Objekte überführt wird. Und im Grunde kann man sich ja - wenn man den Aufwand nicht scheut - offensichtlich tatsächlich durch alle python- Objekte durchhangeln, bis man auf elementaren Daten landet - und daraus den entsprechenden XML- Wust bastelt. Hier rächst sich natürlich der typlose Ansatz ein wenig, macht es also umständlicher.
Dass in den Objekten Objekte/Arrays/Dictionaries stecken, kann ich ausschließen, denn ICH hole ja die Daten aus der Datenbank und baue sie zusammen - und mit einer solchen Einschränkung könnte ich leben (wobei es bestimmt auch eine Möglichkeit gibt, das zu erkennen und sich da durchzuhangeln). Sicher könnte ich das alles über array und dictionarys abwickeln, aber da die Materie komplex genug ist, ist der objektorientierte Ansatz und das Konvertieren vor Übergabe der robustere.
Und nochmal: Die Aufrufe werden doch irgendeine API bedienen also muss man auch genau das übergeben mit genau den Argumenten und Namen die dort gefordert sind. Und nicht ein irgendwie in ein Wörterbuch verfrachtetes Python-Objekt in der vagen Hoffnung, dass das schon irgendwie funktionieren wird. ”Adressen” können nicht übergeben werden, die gelten nur innerhalb eines Prozesses, und ausserdem ist das vom Konzept her unterhalb der Ebene auf der man in Python mit Objekten arbeitet.
Natürlich ist das so, aber in den meisten Fällen entwickelt sich Software doch. Und wenn ich eine universelle Routine habe, die diese Konvertierung vornimmt, dann muss ich mich nur bei den Eingangsdaten darum kümmern - was sowieso nötig ist - und nicht auch noch einmal alle popeligen Hilfsfunktionen, die ich nur brauche, weil die Schnittstellen nicht gut genug miteinander zurechtkommen.

Modul DatenbankInput: class WasIhrWollt
--> ModulVerarbeite: WasIhrWollt nach SoWieEsSeinSoll
--> Modul BestückeWebService: SoWieEsSeinSoll nach WieEsDerWSFrisst
...

Modul DatenbankInput und ModulVerarbeite muss ich sowieso anfassen, wenn Attribute hinzukommen oder entfallen. Warum aber soll ich mich mit dem Modul BestückeWebService noch mal beschäftigen? Das soll ja nur ein Objekt nehmen und den Regeln gemäß weiterwerfen und die Antwort empfangen.
Du zeigst hier syntaktisch falschen Python-Quelltext und irgendwelche allgemeinen Objekte und SOAP-Methoden. Zeig doch mal bitte einen echten Fall, ohne ”Magie” also alles schön ausgeschrieben. Bevor man etwas vereinfacht und automatisiert muss man ja erst einmal das Problem erfassen was man lösen muss, und das habe ich bisher nicht verstanden beziehungsweise habe ich den starken Eindruck Du auch nicht.
Es kann nicht Sinn der Sache sein, ein jedes Problem in voller Detailtiefe zu posten. Das sprengt nicht nur die Befugnisse und Möglichkeiten des Absenders, sondern in der Regel auch die des Empfängers. Natürlich muss man abstrahieren und dazu kommt sicher auch mal nicht lauffähiger Code heraus (auch wenn ich die Syntax- Fehler nicht entdeckt habe).

Die Fragestellung ist also: Wie kann ich ein (nahezu) beliebiges Objekt mittels einer universellen Routine an einen WebService schicken, so dass es von diesem auch korrekt verarbeitet werden kann? Solcherlei Fragestellungen habe ich anderen Programmiersprachen durchaus gelöst und nicht einmal selten. Ich kann mir nicht vorstellen, dass das in python nicht gehen soll.
BlackJack

@am2: Falls sich Objekte serialisieren lassen, dann kann man sie auch in XML serialisieren, das ist schon richtig, aber nicht alle Objekte lassen sich serialisieren. Es gibt welche die sich gegen Introspektion ”wehren” weil der innere Zustand nicht von aussen erreicht werden kann, zum Beispiel weil der Datentyp gar nicht in Python implementiert ist und sich auf irgendwelche Ressourcen ausserhalb von Python stützt. Man landet also nicht zwangsläufig immer bei elementaren Datentypen. Um so etwas allgemein zu unterstützen würde man sich auch nicht per `__dir__`/`dir()` durch die Objekte hangeln sondern den dafür vorgesehenen Mechnanismus zum (de)serialisieren verwenden den zum Beispiel auch `pickle` verwendet.

Das XML welches vom Aufruf zurückkommt und klaglos in Python-Objekte übersetzt wird ist nicht beliebig. Da sind halt die Datentypen als XML serialisiert die SOAP definiert und die werden auf Python-Objekte abgebildet damit man damit in Python arbeiten kann. Und wie so ein Objekt aussieht, also von der Struktur her, sollte einem die API-Dokumentation von dem konkreten SOAP-Aufruf verraten + die Dokumentation der verwendeten SOAP-Bibliothek die verrät wie sie SOAP-Datentypen auf Python-Datentypen abbildet. Genau wie die API-Dokumentation verrät wie die Datenstruktur aussehen muss die man als Argument übergibt.

Ich verstehe echt nicht wie Du einfach so erwarten kannst das man einem sprachunabhängigen RPC-Mechanismus beliebige Objekte für einen Aufruf hinwerfen kann ohne zu schauen was dieser Aufruf überhaupt an Informationen *erwartet*. Das ist in etwa so als wenn Du eine Funktion hast die zwei Argumente für geographische Länge und Breite erwartet (``void frobnicate(double lat, double long)``) und Du beim Aufruf ein Objekt von Typ `NamedPoint` übergibst das ein Feld für den Namen und ein Feld mit einem Array drei Werten für X, Y, und Z enthält und Du irgendwie erwartest, dass irgendwo bei Compilieren oder beim Aufruf schon auf magische Weise das richtige aus diesem Objekt herausgepickt und an die beiden Argumente übergeben wird ohne dass Du das explizit zuordnen müsstest.

„typlose Ansatz” verstehe ich jetzt nicht. Python ist stark typsiert. Jeder Wert hat einen Typ und den kann man ermitteln. Alles was man mit dieser Information zur Übersetzungszeit machen könnte, geht doch auch zur Laufzeit.

Das in den Objekten andere Objekte als die Grunddatentypen stecken kannst Du ja eben *nicht* ausschliessen, denn irgendo kommt ja zum Beispiel schon mal ein `__metadata__`-Attribut ins Spiel was offensichtlich kein Grunddatentyp ist.

Es soll ein Objekt genommen und ”den Regeln gemäss” weitergeworfen werden — da ist mir nicht klar wie die Regel aussehen. An der Stelle würde ich erwarten das Du die Regeln aufstellen musst zum Beispiel in Form einer Abbildung von Attributnamen auf Parameternamen. Das ist der Teil der bei Dir irgendwie auf magische Weise passieren soll ohne das Du Dich für die Parameternamen interessierst die eigentlich vom SOAP-Aufruf erwartet werden. Das verstehe ich nicht wo diese Magie herkommen soll.

Man muss natürlich nicht jedes Problem in voller Detailtiefe darstellen, aber sinnvollerweise mit genug Tiefe damit man es nachvollziehen kann. Genau daran scheitert es bei mir. Für mich klingt das bis jetzt nach: ich brauche eine Funktion die ein belibiges Python-Objekt in etwas umwandelt das man einer beliebigen SOAP-Funktion als Argument übergeben kann. Als Information gibt's das Python-Objekt. Die SOAP-API interessiert nicht. Das klingt für mich *offensichtlich* unmöglich. Und dann gibt es auch IMHO wiedersprüchliche Informationen. Was natürlich auch damit zusammenhängen kann das ich die vorhandenen Informationen falsch deute. Zum Beispiel denke ich es geht darum ein Python-Objekt für einen SOAP-Aufruf zu konverieren. Bibliothek ist `suds`. Das Python-Objekt hat aber ein `__metadata__`-Attribut — etwas was `suds`-Objekte haben die als *Antwort* von einem SOAP-Aufruf zurückgegeben werden. Also haben wir schon einen SOAP-Aufruf hinter uns und gar nicht mehr vor uns!?

Die Fragestellung im letzten Absatz halte ich so immer noch für unmöglich zu lösen. Wenn Du das in anderen Sprachen schon gemacht hast, dann wie denn? Und warum geht das Deiner Meinung dann nicht genau so in Python?

Ich bin mir immer noch nicht sicher ob ich das Problem überhaupt richtig verstanden habe. Kann es sein, dass Du eigentlich ein viel Eingeschränkteres hast? Also zum Beispiel eine SOAP-API mit Aufrufen die nur simple, nichtzusammengesetzte Argumente haben möchte und Du Objekte hast mit simplen, nichtzusammengesetzten Datenattributen die auf diese Argumente abgebildet werden sollen, und zwar vom Namen her 1:1? Wenn der Webservice dann noch eine maschinell lesbare API-Spezifikation als WSDL hat, liesse sich *dass* Problem wahrscheinlich einfach lösen.
am2
User
Beiträge: 17
Registriert: Montag 8. September 2014, 09:57

BlackJack hat geschrieben:@am2: Falls sich Objekte serialisieren lassen, dann kann man sie auch in XML serialisieren, das ist schon richtig, aber nicht alle Objekte lassen sich serialisieren. Es gibt welche die sich gegen Introspektion ”wehren” weil der innere Zustand nicht von aussen erreicht werden kann, zum Beispiel weil der Datentyp gar nicht in Python implementiert ist und sich auf irgendwelche Ressourcen ausserhalb von Python stützt. Man landet also nicht zwangsläufig immer bei elementaren Datentypen. Um so etwas allgemein zu unterstützen würde man sich auch nicht per `__dir__`/`dir()` durch die Objekte hangeln sondern den dafür vorgesehenen Mechnanismus zum (de)serialisieren verwenden den zum Beispiel auch `pickle` verwendet.
Das wäre doch ein Ansatz, oder? Natürlich ergeben sich dadurch Einschränkungen (beispielsweise Attributinhalte, die sich nicht durchschauen lassen wollen. Welches ist den der "vorgesehene Mechanismus" für Grunddatentypen, wie Zeichenketten, Wörterbücher, Zahlen...?
BlackJack hat geschrieben:Das XML welches vom Aufruf zurückkommt und klaglos in Python-Objekte übersetzt wird ist nicht beliebig. Da sind halt die Datentypen als XML serialisiert die SOAP definiert und die werden auf Python-Objekte abgebildet damit man damit in Python arbeiten kann. Und wie so ein Objekt aussieht, also von der Struktur her, sollte einem die API-Dokumentation von dem konkreten SOAP-Aufruf verraten + die Dokumentation der verwendeten SOAP-Bibliothek die verrät wie sie SOAP-Datentypen auf Python-Datentypen abbildet. Genau wie die API-Dokumentation verrät wie die Datenstruktur aussehen muss die man als Argument übergibt.
Ja und? Das ist doch völlig unbestritten. Ich möchte mich halt nur genau EINMAL darum kümmern, der Rest sollte generisch geschehen, das und nichts anderes hätte ich gern.
BlackJack hat geschrieben:Ich verstehe echt nicht wie Du einfach so erwarten kannst das man einem sprachunabhängigen RPC-Mechanismus beliebige Objekte für einen Aufruf hinwerfen kann ohne zu schauen was dieser Aufruf überhaupt an Informationen *erwartet*. Das ist in etwa so als wenn Du eine Funktion hast die zwei Argumente für geographische Länge und Breite erwartet (``void frobnicate(double lat, double long)``) und Du beim Aufruf ein Objekt von Typ `NamedPoint` übergibst das ein Feld für den Namen und ein Feld mit einem Array drei Werten für X, Y, und Z enthält und Du irgendwie erwartest, dass irgendwo bei Compilieren oder beim Aufruf schon auf magische Weise das richtige aus diesem Objekt herausgepickt und an die beiden Argumente übergeben wird ohne dass Du das explizit zuordnen müsstest.
Das habe ich nie behauptet. Natürlich muß ein solcher Aufruf mit GENAU den richtigen Parametern gestartet werden. Und ein anderer Aufruf mit anderen Parametern. Na und? Warum soll ich mich mit so etwas trivialen beschäftigen, wie einer Methode, die ein Objekt serialisiert, das am Ende letztlich doch nur aus Zeichenketten und Zahlen, gestopft in Objekte, Tupel, Listen und Dictionaries. Eine solche Regel ist doch nicht so kompliziert zu beschreiben:
- Objekte, Wörterbücher, Listen und Tupel werden Stück für Stück serialisiert, sie haben entweder Namen (Objekte oder Wörterbücher) oder Positionen (Listen, Tupel)
- elementare Objekte (Zahlen, Zeichenketten etc.)
Jetzt sag nicht, dass Deine Phantasie nicht reicht, aus einem Objekt mit 5 Attributen, von denen eines eine Liste und eines ein Wörterbuch ist, ein "sinnvolles" XML zu machen!
BlackJack hat geschrieben:„typlose Ansatz” verstehe ich jetzt nicht. Python ist stark typsiert. Jeder Wert hat einen Typ und den kann man ermitteln. Alles was man mit dieser Information zur Übersetzungszeit machen könnte, geht doch auch zur Laufzeit.
Das schon, aber Du kannst zur Entwicklungszeit nicht wissen, was zur Laufzeit so drin stecken wird. Das bedeutet, dass Du noch universeller herangehen musst.[/quote]
BlackJack hat geschrieben:Das in den Objekten andere Objekte als die Grunddatentypen stecken kannst Du ja eben *nicht* ausschliessen, denn irgendo kommt ja zum Beispiel schon mal ein `__metadata__`-Attribut ins Spiel was offensichtlich kein Grunddatentyp ist.
Genau. Daher alles, was nicht geht, ignorieren. Natürlich, wenn ich irgendwann man genau auf dieses __metadata__ angewiesen sein sollte, muss ich an der Funktion schrauben. Aber die allgemeinen Fälle, wie oben beschrieben, könnte sie abdecken.
BlackJack hat geschrieben:Es soll ein Objekt genommen und ”den Regeln gemäss” weitergeworfen werden — da ist mir nicht klar wie die Regel aussehen. An der Stelle würde ich erwarten das Du die Regeln aufstellen musst zum Beispiel in Form einer Abbildung von Attributnamen auf Parameternamen. Das ist der Teil der bei Dir irgendwie auf magische Weise passieren soll ohne das Du Dich für die Parameternamen interessierst die eigentlich vom SOAP-Aufruf erwartet werden. Das verstehe ich nicht wo diese Magie herkommen soll.
Die Regeln zu einer solchen Serialisierung findest Du oben. Magie ist da nicht das richtige Synonym.
BlackJack hat geschrieben:Man muss natürlich nicht jedes Problem in voller Detailtiefe darstellen, aber sinnvollerweise mit genug Tiefe damit man es nachvollziehen kann. Genau daran scheitert es bei mir. Für mich klingt das bis jetzt nach: ich brauche eine Funktion die ein belibiges Python-Objekt in etwas umwandelt das man einer beliebigen SOAP-Funktion als Argument übergeben kann.
ja zu jedes beliebige python- Objekt
nein zu jede beliebige SOAP- Funktion

Stell Dir eine Funktion FüttereSoapMitObjekt(object,soapclient) vor. Die macht - gemäß oben genannter Regeln - ein XML aus dem Objekt und wirft es dem übergebenen soap- Client zum Fraß vor. Das Ergebnis schmeißt sie zurück.

Der Aufruf erfolgt dann so:
AdresseVonLiese = FüttereSoapMitObjekt(Person, AdressClient)
Landrat = FüttereSoapMitObjekt(Landkreis, PolitikClient)

Ich müsste im Grunde nichts an der Funktion FüttereSoapMitObjekt tun. Natürlich muss ich aber Person so gestalten, dass sie zu AdressClient passt, sowie Landkreis so, dass er zu PolitClient passt.
Als Information gibt's das Python-Objekt. Die SOAP-API interessiert nicht. Das klingt für mich *offensichtlich* unmöglich. Und dann gibt es auch IMHO wiedersprüchliche Informationen. Was natürlich auch damit zusammenhängen kann das ich die vorhandenen Informationen falsch deute. Zum Beispiel denke ich es geht darum ein Python-Objekt für einen SOAP-Aufruf zu konverieren. Bibliothek ist `suds`. Das Python-Objekt hat aber ein `__metadata__`-Attribut — etwas was `suds`-Objekte haben die als *Antwort* von einem SOAP-Aufruf zurückgegeben werden. Also haben wir schon einen SOAP-Aufruf hinter uns und gar nicht mehr vor uns!?
Korrekt erkannt. Ich habe "zu Fuß" ein Objekt in ein Dictionary gewandelt, das hat suds brav in ein offensichtlich geeignetes XML umgewandelt, so dass ich eine korrekte Antwort mit einem relativ komplexen Objekt vom Webservice erhielt.
Und um mein "zu Fuß" zu verbessern, habe ich es mit diesem Objekt getestet. Theoretisch könnte dieses Objekt ja auch Eingangsdatum für einen weiteren WebService dienen.
Woher das Testobjekt kommt, ist also unerheblich.
Die Fragestellung im letzten Absatz halte ich so immer noch für unmöglich zu lösen. Wenn Du das in anderen Sprachen schon gemacht hast, dann wie denn? Und warum geht das Deiner Meinung dann nicht genau so in Python?
In Python fehlen mir einfach die Mittel. In den Borland- Sprachen gibt es RTTI, womit man das meiste realisieren kann und in VB gab es auch irgendwas, mit dem ich mich durch Objekte durchhangeln konnte.
Natürlich klappt das nur wirklich pragmatisch, wenn man mit streng hierarchischen Objekten zu tun hat. Wenn es sich um Netze handelt, braucht man natürlich komplexere Strategien.
Ich bin mir immer noch nicht sicher ob ich das Problem überhaupt richtig verstanden habe. Kann es sein, dass Du eigentlich ein viel Eingeschränkteres hast? Also zum Beispiel eine SOAP-API mit Aufrufen die nur simple, nichtzusammengesetzte Argumente haben möchte und Du Objekte hast mit simplen, nichtzusammengesetzten Datenattributen die auf diese Argumente abgebildet werden sollen, und zwar vom Namen her 1:1? Wenn der Webservice dann noch eine maschinell lesbare API-Spezifikation als WSDL hat, liesse sich *dass* Problem wahrscheinlich einfach lösen.
Vielleicht kurz etwas zum Kontext:
Ich gebe geographische Informationen an den Web- Service (derzeit simpel x,y im Gauss/Krüger- Format) und dieser gibt mir (mehr oder minder komplexe) Objekte zurück, die sich dort in der Nähe befinden. Diese Objekte können sehr unterschiedlich sein, sagen wir mal Pflanzen, Häuser und Seen. Und vielleicht will ich mal einen Baum nehmen und an einen anderen Web- Service übergeben, der mir den Wert dieses Baumes zurückgibt. Da sich das Baum- Objekt aber immer verändert, muss natürlich auch die API angepasst werden, keine Frage. Aber eigentlich muss ich meinen Code nicht ändern, weil der Baum von der selben Stelle zurückgegeben wird, der auch den zweiten WebService baut. Meine Aufgabe erschöpft sich vielleicht darin, die Summe der Werte aller Bäume in einem festgelegten Gebiet zu ermitteln. Da ist es mir völlig egal, ob der Baum ein Attribut "Anzahl der Äste" hat.
Eigentlich ist mein Problem teilweise etwas spezieller (es geht um Straßen) und teilweise etwas allgemeiner (Ich weiß im Einzelfall nicht unbedingt, was ich bekomme, ich weiß nur, wohin ich das weiterwerfen muss.
Das Ganze hat etwas mit geographischen Informationen und FME zu tun. Es geht darum, ein Netz auf ein anderes abzubilden. Dazu sind verschiedene Objekte (des wirklichen Lebens) zu berücksichtigen (wenn beispielsweise in einem Netz ein Schild an der Position 100,100 steht und im anderen an 102,98 --> Dann kann es nötig sein, zu vermitteln/zu verzerren. Es ist mir aber völlig egal, was das für ein Schild ist, wie es modelliert ist - ich will es nur identifizieren.)

Und im Moment möchte ich mir nur die Arbeit erleichtern, WebServices mit Objekten zu beschicken. Das andere ist schon in weiten Bereichen fertig und erprobt, muss aber eben auch noch universeller gemacht werden, weil ich gern "jedes beliebige Netz" damit verarbeiten will.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

am2 hat geschrieben: Und im Moment möchte ich mir nur die Arbeit erleichtern, WebServices mit Objekten zu beschicken. Das andere ist schon in weiten Bereichen fertig und erprobt, muss aber eben auch noch universeller gemacht werden, weil ich gern "jedes beliebige Netz" damit verarbeiten will.
Und genau das ist einfach unmöglich, weil es dafür eben Regeln braucht - und die fallen ja eben nicht vom Himmel, sondern sind eben abhängig vom Domänenobjekt auf Domänenseite und der WSDL auf SOAP-Seite. Eine solche Abbildung kann universell nicht funktionieren. Punkt. :-) Imho ist es da einfacher, wenn man sich geeignete Factory / Builder-Objekte baut, die eine Konvertierung zwischen Domänenobjekten und SOAP-Request / -Response-Objekten bereit stellen.

Nehmen wir mal folgendes Beispiel bestehend aus zwei JSON-Notationen:

Code: Alles auswählen

{
    "name": "Pizza Salami"
    "price": 6.99
    "ingredients": [
        "Salami",
        "Cheese",
        "Tomatosauce"
    ]
}

{
    "dish": "Pizza Salami"
    "price": "6,99 €"
    "fixings": [
        "Salami",
        "Cheese",
        "Tomatosauce"
    ]
}
Wie kommt man nun von der ersten zur zweiten, *ohne* spezielle Informationen über die jeweilige Abbildung zu definieren? Und das ist ein einfacher Fall, da die beiden Objekte strukturell gleich sind. Was, wenn beim ersten noch ein boolesches Feld ``is_veggie`` dazu kommt, was es im zweiten gar nicht gibt? Oder noch besser, wenn die Zutatenstruktur bei einem gar keine Liste, sondern ein komplexe Struktur mit Angaben zur Menge o.ä. ist?

Vielleicht kannst Du das an diesem einfachen Beispiel erläutern, dann kapieren wir vermutlich, was Du genau planst :-)

Wenn man derartig fest mit solchen Webservices verzahnt arbeitet, so könnte man ggf. auch direkt mit den SOAP-basierten Typen-Objekten arbeiten; besonders unterschiedlich fallen die ja sicherlich nicht aus, ansonsten wären sie ja gar keine geeigneten Abstraktionen für einen solchen Service.

Natürlich muss man dann immer per Hand Anpassungen vornehmen, wenn sich etwas am Service ändert - aber davor ist man ja nie gefeit, in keiner Sprache!

Für den direkten Transfer von Objekten zwischen SOAP-Webservices existiert übrigens BPEL (noch abartiger als SOAP :twisted: - aber eben genau dafür entworfen!)

Unter den "oben definierten" Reglen kann ich mir auch nichts vorstellen. Worin bestehen diese denn konkret? Und worin besteht der Vorteil gegenüber maßgeschneiderten Parsern / Generatoren?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@am2: Objekte allgemein als XML serialisieren ist kein Ansatz denn damit muss die Gegenseite von dem SOAP-Aufruf ja auch etwas anfangen können, also jeder Aufruf den man so absetzt muss genau ein Argument erwarten und zwar dieses XML-Dokument. Als Typ Zeichenkette‽ Das würde irgendwie den Sinn von SOAP unterlaufen.

Der „vorgesehene Mechanismus” ist der den `pickle` benutzt. An diese API hängt man sich normalerweise wenn man etwas zu (de)serialisieren selber schreiben möchte was genau so mächtig ist wie `pickle`. Ich sehe in diesem Kontext aber nicht was das bringen sollte.

Meine Phantasie reicht aus um mir vorzustellen wie man aus solchen einfachen Objekten XML machen kann. Das wäre auch trivial umzusetzen. Sie reicht nicht mehr aus zu sehen wie das dann zum SOAP-Aufruf passen soll, denn so einfache Objekte hast Du ja nicht. Da sind ja offenbar noch andere Attribute enthalten und genau über die stolperst Du gerade. Der Ansatz ein beliebiges Objekt vorzubereiten funktioniert nur wenn man explizit beschreibt welche Attribute berücksichtigt werden sollen. Diese Information kann man sich falls vorhanden aus einer WSDL-Beschreibung der API holen. Sonst hat man Pech gehabt und muss mindestens die Zuordnung Typ zu Attributen halt irgendwo einmal hinschreiben. Alles andere würde erfordern das man Code schreiben kann der ohne weitere Informationen erraten kann was für Parameter ein gegebener SOAP-Aufruf erwartet.

Bei der `FüttereSoapMitObjekt()` verstehe ich das Problem jetzt nicht, *ausser* dass das Objekt Attribute haben darf welche die Funktion nicht erwartet. Du musst halt eine Datenstruktur erstellen die nur aus Python-Grunddatenstrukturen besteht die SUDS dann für Dich zu SOAP-Datentypen macht und verschickt.

Woher das Testobjekt kommt ist nicht unerheblich denn die SUDS-Objekte sind ja etwas anderes als ganz simple Objekte. Da stecken ja noch alle möglichen Informationen über diesen Typ aus Sicht von SOAP drin. Auf der anderen Seite müsstest Du bei *diesen* Objekten eigentlich gar nichts ändern. Wenn Du so ein Objekt von einem SOAP-Aufruf zurückbekommst das im WSDL zum Beispiel als SOAP-Typ `Baum` definiert ist und eine andere Methode des gleichen Service einen Soap-Typ `Baum` erwartet, dann hast Du doch bereits ein Objekt vom passenden Typ vor Dir!

Durch die Objekte hangeln kann man sich in Python ganz prima. Das Problem ist hier aber welche davon sollen in die Datenstruktur für den Aufruf und welche nicht. Die Beschreibung der *erwarteten* Struktur liegt schon als WSDL vor, also sollte man versuchen das zu nutzen.
am2
User
Beiträge: 17
Registriert: Montag 8. September 2014, 09:57

Meine letzte Anmeldung habe ich leider vergessen, daher habe ich mich neu angemeldet. Aber eines ist mir damals schon in Erinnerung geblieben: Bei allem, was ich gelernt habe - wofür ich dankbar bin - immer wird eine Frage "WIE" mit einer Frage "WARUM" beantwortet bzw. der Sinn der Fragestellung angezweifelt.

Es ist in Ordnung für mich, wenn Du sagst, dass Du es für unmöglich hältst und das sogar begründest. Aber es ist - gelinde gesagt - irritierend, dass Du so vehement den Sinn anzweifelst.

Zu Deinen JSON- Objekten: Natürlich würde das zugehörige XML bei beiden völlig anders aussehen, auch wenn es inhaltlich identisch auszusehen scheint. DAS ist aber auch nicht wichtig. Denn das Objekt selbst wird - wie beschrieben - an anderer Stelle schon so gebastelt, das es zum WebService passt (bzw. es kommt aus einem Web- Service und wird von da aus weiterverarbeitet). Wenn also mein WS mit der ersten Definition spielen will, dann bekommt er eben diese. Ich will mich aber nicht noch einmal darum kümmern müssen, nur um die Schnittstelle bedienen zu können. Im Objekt stecken schon alle Informationen, die der WebService braucht, es muss nur noch adäquat in ein passendes XML umgeformt werden. Auf den Hersteller der WebServices habe ich Einfluß, wir können von beiden Seiten so bauen, dass es einen möglichst geringen Aufwand darstellt.

Wenn das Beispiel mit den Bäumen nicht funktioniert, dann geht mir langsam die Puste aus. Ich versuch es dennoch nochmal mit einem neuen Beispiel (und bitte frag nicht nach dem tatsächlichen Fall, ehe ich das beschrieben habe und Du das gelesen hast, sind Tage um)

Beispiel (abstrakt)

Ich habe die Fläche eines Flurstückes [(x,y),(x,y),...] und brauche ein Verfahren, um den Wert aller relevanten Objekte zu ermitteln und einen Gesamtwert zu erstellen.

Ich gebe die Fläche an einen Web- Service (A) und bekomme alle Objekte zurück, die grob in diesem Gebiet liegen [(x,y,<irgend ein Objekt>),(x,y,<irgend ein Objekt>),...]
Von diesen schmeiße ich einige raus, weil sie entweder nicht innerhalb der Fläche liegen oder für die Betrachtung uninteressant sind/noch kein Bewertungsverfahren existiert
Dann arbeite ich einige Objekte auf, indem ich die Informationen dranfüge, die die Web- Services (B1, B2, B3...) benötigen (vielleicht auch mal Objekte lösche), das könnten Wertminderungsfaktoren sein, die sich aus Alter, irgendeiner Lage zueinander oder anderen Dingen ergeben. Dazu will ich natürlich nicht im XML rumkritzeln, sondern die Vorzüge der Objekte nutzen, wobei MICH nur ein Teil der Informationen interessiert, die WS B1...Bn interessieren sich für alles, was übrig bleibt.
Wenn die Objekte aufbereitet sind, dann schicke ich Bäume an B1, Häuser an B2 ... und erhalte zu jedem Objekt eine Zahl zurück.
Im letzten Schritt möchte ich diese nur noch addieren und liefere das Ergebnis.

Was kann alles passieren?
- Da sich die Software, die die eigentlichen Daten hält sowie die, die die Bewertung durchführt, weiterentwickelt, ändern sich die Web- Services unabhängig von mir. Solange das nur Attribute betrifft, mit denen ich nichts tue, kann mir das auch völlig egal sein. An meinem "Programm" muss ich nichts tun (jdenfalls nicht, solange die WS da stehen, wo sie standen, die Funktionen heißen, wie sie hießen, alle Informationen, die ich brauche, noch vorhanden sind und die Objekte, die A wirft, immer noch zu den Objekten, die B1..Bn erwartet, passen).
- Da sich die Bewertungsstrategie ändern könnte (Es kommen vielleicht Abmilderungsfaktoren für Windparks, Hochwasserchutzgebiete o.ä. hinzu), muss ich mein Programm gelegentlich anpassen. Das hat aber nicht notwendigerweise Einfluss auf die Haltung der Objekte (A) oder die Berechnungsverfahren für den Wert (B1..Bn)

Das heißt, dass sich beide Sphären bis zum gewissen Grad unabhängig voneinander entwickeln können sollen. Und um das zu realisieren, DARF ich mich nicht dafür interessieren, wie ein Objekt konkret aussieht. Es muss nur auf beiden Seiten klar sein, wie der Transfer pythonObjekt -> XML -> BlaBlaBlaObjekt und BlaBlaBlaObjekt -> XML -> pythonObjekt ausssehen.
Und mich interessiert HIER der Transfer XML -> pythonObjekt (der wie von Geisterhand funktioniert) und der pythonObjekt -> XML (der eben im Moment SO nicht funktioniert). Für letzteres benötige ich ein Verfahren. Wenn es sein muss, dann kann ich auch erzwingen, dass alle relevanten Attribute mit XYZ beginnen sollen. Das ist mir völlig Wurst, Hauptsache, ich muss mich EBEN NICHT weiter um den Webservice kümmern.

Ich hoffe, es ist etwas klarer geworden.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

am2 hat geschrieben: Und mich interessiert HIER der Transfer XML -> pythonObjekt (der wie von Geisterhand funktioniert) und der pythonObjekt -> XML (der eben im Moment SO nicht funktioniert).
Ah :idea: Du suchst also das, was man in dem Kontext afaik "Stub"-Objekt nennt. In der Java- und C#-Welt existieren zig Tools, die Dir aus der WSDL solche Klassen generieren; eben einmal für den Request und einmal für die Response. Offenbar scheinst Du etwas für letzteres zu haben und Dir fehlt etwas für ersteres. Nun, das will man auch nicht von Hand machen! Leider kann ich Dir da für Python kein Tool nennen; aber auf demselben Weg, wie Du an das eine gekommen bist, sollte es auch möglich sein, an das andere zu gelangen. Auch wenn SOAP in der Python-Welt nicht so wirklich groß ist, sollte es da doch etwas geben! Ohne das ist SOAP ja noch unerträglicher :mrgreen:
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@am2:
In der Doku zu suds steht doch, wie man aus den WSDL-Typen Stub-Objekte kreiert und mit Daten versieht - https://fedorahosted.org/suds/wiki/Documentation
Funktioniert das bei Dir nicht?
BlackJack

@am2: XML → Python-Objekt funktioniert wie von Geisterhand weil in der WSDL-Beschreibung für den Webservice die Typen beschrieben sind. Das sind dann auch nicht beliebige Python-Objekte sondern welche die neben den Attributen die Dich interessieren noch zusätzliche Informationen zu der dazugehörigen WSDL-Typdefinition haben. Deshalb kann man diese Objekte auch wieder als Argumente für SOAP-Aufrufe benutzen, denn die tragen die nötigen Metainformationen mit sich um wieder für SOAP als XML serialisiert werden zu können.

Man kann mit `client.factory.create('typname')` ”leere” Objekte erstellen für im WSDL definierte SOAP-Typen. Also auch komplexe verschachtelte Objekte bei denen alle einfachen Typen mit `None` vorbelegt sind. Die Attribute kann man dann zuweisen und so ein Objekt als Argument für einen SOAP-Aufruf verwenden weil da schon die nötigen Informationen von/für `suds` drin stecken um die zu serialisieren.

Wenn man übrigens auf beide Seiten einfluss hat, und es möglichst einfach machen möchte, dann verwendet man kein SOAP. ;-)
Antworten