Vereinfachtes "Typechecking" möglich?

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.
Antworten
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Hoi,

eigentlich soll meine Applikation von mir was folgt mehr oder weniger automatisch berechnen, aber Nutzer sollen doch auch die Möglichkeit bekommen einen Sack voll Paramter per Hand an eine Funktion zu geben. So ein Haufen Paramter kann, je nach Fragestellung an das Programm rel. lang aussehen, z. B. so:

Code: Alles auswählen

{'A': {'phi': 8, 'psi': -6, 'dz': 4, 'dx': -1.0, 'dy': -5, 'theta': 2.0}, 'C': {'phi': 8, 'psi': 8, 'dz': 5, 'dx': 5, 'dy': -5, 'theta': 8}, 'B': {'phi': 0, 'psi': 6, 'dz': -5, 'dx': 5, 'dy': 5, 'theta': 8}, 'E': {'phi': 4, 'psi': 0, 'dz': 5, 'dx': 2, 'dy': 5, 'theta': 8}, 'D': {'phi': 8, 'psi': 8, 'dz': 5, 'dx': 4, 'dy': -4, 'theta': -2}, 'G': {'phi': 8, 'psi': 8, 'dz': 1, 'dx': 5, 'dy': -1.0, 'theta': 8}, 'F': {'phi': 0, 'psi': 8, 'dz': -5, 'dx': 5, 'dy': -2, 'theta': 8}, 'H': {'phi': 8, 'psi': 6, 'dz': 5, 'dx': 4, 'dy': 5, 'theta': 8}}
Das von Hand einzugeben, ist also wirklich nur die letzte Lösung, aber das Angebot richtet sich auch dahingehend, das Nutzer der Programms aus einem Logfile schnell mal was visualisieren können - wenn sie es wollen. Allerdings ist es natürlich leicht passiert, das hier bei der Eingabe ein Fehler unterläuft, also steht in einer Funktion - daher die weite Einrückung - folgender Code:

Code: Alles auswählen

        if move:
            # check at least whether move is a dict
            if not isinstance(move, dict):
                raise AssertionError("given parameter 'move' is no dictionary, see documentation for" +
                                                " further details")
            for chain, params in move.iteritems():
                if not isinstance(params, dict):
                    raise AssertionError("entry for chain %s is no dictionary" % chain)
                for param, value in params.iteritems():
                    if not isinstance(value, (float, int)):
                        raise AssertionError("entry for %s in chain %s is not number (float or int)" %
                                                             (param, chain))
Meine Frage: Gibt es für dieses Konstrukt auch eine Abkürzung? Es sieht so unschön aus ...

Gruß,
Christian
Benutzeravatar
lutz.horn
User
Beiträge: 205
Registriert: Dienstag 8. November 2005, 12:57
Wohnort: Pforzheim

Greif doch einfach mit get() auf das vermutete Dictionary zu. Falls move kein Dictionary ist, wird ja ein AttributeError geworfen. Ebenso werfen int(x) und float(x) einen ValueError, falls ihr Argument nicht verarbeitet werden kann.

Siehe auch http://de.wikipedia.org/wiki/Dynamische ... uck_Typing

Der Nachteil dabei ist natürlich, dass nicht die anwendungsspezifischen Fehlertexte ausgegeben werden.
https://www.xing.com/go/invite/18513630.6a91d4
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

Moin!
CM hat geschrieben:Meine Frage: Gibt es für dieses Konstrukt auch eine Abkürzung? Es sieht so unschön aus ...

Code: Alles auswählen

assert isinstance(move, dict),'move is no dictionary'
So umgehst du das ganze if/raise-Zeugs. Ansonsten kann ich auch nur empfehlen, nicht alles überprüfen zu wollen. Lieber an den richtigen Stellen auftretende Exceptions abfangen, wie das lutz.horn empfohlen hat.

Gruß,
Manuel
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

CM hat geschrieben:Gibt es für dieses Konstrukt auch eine Abkürzung?
Hallo Christian!

Eine Abkürzung ist das hier nicht unbedingt, aber es ist sicher schneller als die vielen Tests die du machst. Hier wird nur mehr darauf reagiert, wenn sich die Daten nicht wie gewünscht verhalten. Es wird nicht vorausgesetzt dass es Dictionaries sind. So lange sie sich wie Dictionaries verhalten ist alles in Ordnung.

Code: Alles auswählen

if move:
    try:
        for chain, params in move.iteritems():
            try:
                for param, value in params.iteritems():
                    value = float(value)
                    # ...
                    # ...
            except AttributeError:
                raise AssertionError("entry for chain %s is no dictionary" % chain)
            except ValueError:
                raise AssertionError(
                    "entry for %s in chain %s is not number (float or int)" %
                    (param, chain)
                )
    except AttributeError:
        raise AssertionError(
            "given parameter 'move' is no dictionary, see documentation for "
            "further details"
        )
Vielleicht kannst du ja etwas damit anfangen.

lg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

lutz.horn hat geschrieben:Der Nachteil dabei ist natürlich, dass nicht die anwendungsspezifischen Fehlertexte ausgegeben werden.
Gut, das sollte aber der Fall sein - der Nutzer soll nicht unbedingt Python *können* müssen - und das Ganze soll rel. idiotensicher sein.
Im Übrigen sollte ich wohl hinzufügen, daß das dict u. a. deshalb so kompliziert ist, um auf der anderen Seite möglichst flexibel rechnen zu können. Wie man vielleicht sehen kann, sind die Parameter Bewegungsparamter und mein Ziel ist es für die Chains ('A', 'B', usw.) jede (!) Symmetrie abbilden zu können, die diese Partikel einnehmen können - nicht nur Schönfliessymmetrien. Das heißt leider auch sogar, daß es keinen Chain 'A' geben muß. Die Bennennung ist willkürlich. Was soll ich also mit get() holen? (Ich könnte natürlich erst testen: dict, ja oder nein? Und dann ggf. mit keys auf die Schlüssel zugreifen - aber das ist genauso umständlich, wie der Code, der da steht.)

Gruß,
Christian
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Ah, Gerold, das ist eine gute Idee! Kürze ist vielleicht nicht so entscheidend, aber Dein Snippet ist deutlich eleganter ;-).

Merci,
Christian
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

helduel hat geschrieben:

Code: Alles auswählen

assert isinstance(move, dict),'move is no dictionary'
So umgehst du das ganze if/raise-Zeugs.
"assert" ist nur zum Testen gedacht und nicht zum Implementieren von Logik, da beispielsweise bei Erzeugung von optimiertem Code alle assert-Statements entfernt werden.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

helduel hat geschrieben:Ansonsten kann ich auch nur empfehlen, nicht alles überprüfen zu wollen.
Sorry, aber hier muß ich noch mal Stellung nehmen: Das mache ich doch, da - wie beschrieben - es sich um eine an einen pot. Nutzer einer Applikation gerichtete Eingabefunktion handelt. Wenn ich nicht an der Stelle fehlerhafte Eingaben mit sinnvollen Meldungen abfange, dann kann ein unbedarfter Nutzer mit einem laaangen Traceback konfrontiert werden, bei dem dann stünde, daß in einer Funktion _rotate() (Man beachte das '_' das absichtlich voransteht, weil es sich um etwas handelt, wo ich absichtsvoll einen Wrapper drumgebastelt habe.) ein Fehler auftrat. Und dann soll dieser Nutzer den Fehler finden können? Die Mailinglisten sind voll von Anfrage à la: "Ich habe da dieses ewiglange Traceback: Ist das ein Bug in eurer Software? Wo liegt der / mein Fehler? usw."
DAS zeugt ggf. von schlechtem Design. Insbesondere, wenn es sich um rechenintensive Software handelt, wo der Traceback dann im Zweifelsfall erst nach geraumer Zeit kommt - insofern muß ich auch noch mal darüber nachdenken, ob ich Gerolds Lösung tatsächlich so gut für mich finde. (Wußte doch, daß ich mir was dabei gedacht hatte nicht mit try/except zu arbeiten ... gehört unbedingt als Kommentar rein, wenn ich mich gegen Gerolds Version entscheide.)
Schlechte pythonbasierte Beispiele mit Mailinglisten mit entsprechenden Anfragen:
http://www.salilab.org/modeller/
http://autodock.scripps.edu/
(Beides tolle Programme, aber Fehlersuche kann z. T. furchbar sein.)
Look before you leap ist rel. unpythonisch, kann aber doch manchmal sinnvoll sein - daher mein langes Plädoyer (obwohl wirklich erst noch überlegen muß, was bei meinem Programm am besten ist).

Zu dem "assert" sage ich jetzt mal nichts - wie ich sehe hat das zwischenzeitlich schon ein Anderer getan ... ;-)

Gruß,
Christian
BlackJack

Vielleicht könntest Du das von der Funktion trennen und eine Extrafunktion zum überprüfen der Benutzereingabe einfügen. Benutzereingaben muss man natürlich so gut wie möglich überprüfen, man ahnt ja meist gar nicht auf was für Ideen die so kommen können. ;-)
Zap
User
Beiträge: 533
Registriert: Freitag 13. Oktober 2006, 10:56

EyDu hat geschrieben:"assert" ist nur zum Testen gedacht und nicht zum Implementieren von Logik, da beispielsweise bei Erzeugung von optimiertem Code alle assert-Statements entfernt werden.
Wo kann man mehr über diese Verhaltensweise erfahren?!
Von welcher Optimierung redest du? Gibt's automatisierte Optimierungen die das in der Realität machen?

Ich bin hier und da durchaus geneigt asserts einzubauen
sollte man stattdessen lieber "if foo: raise MyException("bar") " verwenden?
Finde ich auch nicht wirklich optimal weil manchmal Kontrollen nichts anderes für mich sein sollten als "asserts".
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Zap hat geschrieben:Wo kann man mehr über diese Verhaltensweise erfahren?!
Von welcher Optimierung redest du? Gibt's automatisierte Optimierungen die das in der Realität machen?[/qoute]
Lies dir hier dazu einfach mal den letzen Absatz durch.
Zap hat geschrieben:Ich bin hier und da durchaus geneigt asserts einzubauen
sollte man stattdessen lieber "if foo: raise MyException("bar") " verwenden?
Wenn du die asserts dazu verwendet zu prüfen ob dein Programm korrekt funktioniert (automatische Tests, Unittests, ...) verwendest du sie richtig. Jede andere Verwendung ist nicht sinnvoll.

Dient die Überprüfung hingegen deinem Programm, so ist ein "if...: raise..." die richtige Wahl.
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

CM hat geschrieben: Sorry, aber hier muß ich noch mal Stellung nehmen: Das mache ich doch, da - wie beschrieben - es sich um eine an einen pot. Nutzer einer Applikation gerichtete Eingabefunktion handelt.
Wenn der Nutzer falsche *Typen* (nicht Werte) übergeben kann, ist er Programmierer, denn dann hat er Code geschrieben, der mit deinem interagiert. Dann aber kann man zumindest verlangen, dass er die Dokumentation liest (die wiederum hoffentlich für öffentliche Schnittstellen existiert).
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

CM hat geschrieben:
helduel hat geschrieben:Ansonsten kann ich auch nur empfehlen, nicht alles überprüfen zu wollen.
[...]
DAS zeugt ggf. von schlechtem Design. Insbesondere, wenn es sich um rechenintensive Software handelt, wo der Traceback dann im Zweifelsfall erst nach geraumer Zeit kommt - insofern muß ich auch noch mal darüber nachdenken, ob ich Gerolds Lösung tatsächlich so gut für mich finde. (Wußte doch, daß ich mir was dabei gedacht hatte nicht mit try/except zu arbeiten ... gehört unbedingt als Kommentar rein, wenn ich mich gegen Gerolds Version entscheide.)
[...]
Zu dem "assert" sage ich jetzt mal nichts - wie ich sehe hat das zwischenzeitlich schon ein Anderer getan ... ;-)
Da habe ich wohl deinen Code-Schnipsel nicht richtig verstanden. Natürlich ist 'assert' nur zum Testen da. Ich ging nicht davon aus, dass der User sich darum kümmern soll, dass die Daten irgendwie ordentlich in Dictionaries verpackt daherkommen, sondern dass das deine Applikation macht. Deswegen meinte ich, nicht alles überprüfen.

Gruß,
Manuel
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

EyDu hat geschrieben: Lies dir hier dazu einfach mal den letzen Absatz durch.
Warum verlinkst du gegen die Doku zu 1.5.2? Das hier wäre wesentlich aktueller.

@Zap: Gemeint ist hier die "Optimierung", die beim Aufruf von python -O durchgeführt wird.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

birkenfeld hat geschrieben:Warum verlinkst du gegen die Doku zu 1.5.2?
Und genau so etwas passiert, wenn man Dokumentation über Google sucht und den Ergebnissen einfach mal blind vertraut :roll:

Ich werde die aktuelle Doku dann einfach mal in meine Favoriten legen.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

BlackJack hat geschrieben:Vielleicht könntest Du das von der Funktion trennen und eine Extrafunktion zum überprüfen der Benutzereingabe einfügen. Benutzereingaben muss man natürlich so gut wie möglich überprüfen, man ahnt ja meist gar nicht auf was für Ideen die so kommen können. ;-)
Oh, jaaa :roll:
Aber bisher ist die in Frage kommende Funktion die Einzige, die zu diesen rel. low-level-Zugriff ermuntert. ... Prinzipiell aber hast Du recht: Auch das sollte ich überdenken. Danke.
birkenfeld hat geschrieben:Wenn der Nutzer falsche *Typen* (nicht Werte) übergeben kann, ist er Programmierer, denn dann hat er Code geschrieben, der mit deinem interagiert. Dann aber kann man zumindest verlangen, dass er die Dokumentation liest (die wiederum hoffentlich für öffentliche Schnittstellen existiert).
Berechtigter Einwand, aber meine pot. Nutzer sind Wissenschaftler, die alle glauben programmieren zu können und ungerne Doku lesen ... ;-) (die Doku erstelle ich gerade erst :oops: )
helduel hat geschrieben:Ich ging nicht davon aus, dass der User sich darum kümmern soll, dass die Daten irgendwie ordentlich in Dictionaries verpackt daherkommen, sondern dass das deine Applikation macht.
meine Antwort
CM hat geschrieben:eigentlich soll meine Applikation von mir was folgt mehr oder weniger automatisch berechnen, aber Nutzer sollen doch auch die Möglichkeit bekommen einen Sack voll Paramter per Hand an eine Funktion zu geben.
Anstelle von "eine Funktion" sollte es wohl "eine bestimmte Funktion" lauten. Aber die Diskussion und meine Antworten leiten mich zum Gedanken, daß ich auch an der Stelle das Design überdenken sollte ...

Auweia, hätte ich gewußt, daß ich sooo eine Diskussion losschiebe ... ist aber eigentlich ganz interessant.

Gruß,
Christian
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

birkenfeld hat geschrieben:Wenn der Nutzer falsche *Typen* (nicht Werte) übergeben kann, ist er Programmierer, denn dann hat er Code geschrieben, der mit deinem interagiert. Dann aber kann man zumindest verlangen, dass er die Dokumentation liest (die wiederum hoffentlich für öffentliche Schnittstellen existiert).
Berechtigter Einwand, aber meine pot. Nutzer sind Wissenschaftler, die alle glauben programmieren zu können und ungerne Doku lesen ... ;-) (die Doku erstelle ich gerade erst :oops: )
[/quote]

In dem Fall wäre aber auch eine saubere Lösung z.B. mit Dekoratoren gut:

Code: Alles auswählen

@takes(str, dict)
def foo(...)
Das hat den Vorteil, dass sich der Overhead fürs Typen prüfen trivial abschalten lässt, indem man nur `takes` umdefiniert.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Das ist in der Tat eine interessante Option, aber ich muß zugeben: Hier stoßen meine Pythonfähigkeiten seit langem wieder an eine Grenze. Wie müßte denn takes aussehen, wenn

Code: Alles auswählen

def foo(self, par0, par1=default_integer, par2=default_list, par3=verschachteltes_default_dict):
mit par0 einem x-beliebigen Argument entsprechend, z. B. ein String (weil der in der Auflistung fehlt ;-) ).

Außerdem, und hier wird es wichtig, wie sieht es mit Dekoratoren in Python 3k aus? Ich habe gesucht und nichts gefunden, vermute aber, daß ich bloß zu blöd zum finden war ...

Gruß,
Christian
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

CM hat geschrieben:Das ist in der Tat eine interessante Option, aber ich muß zugeben: Hier stoßen meine Pythonfähigkeiten seit langem wieder an eine Grenze. Wie müßte denn takes aussehen, wenn

Code: Alles auswählen

def foo(self, par0, par1=default_integer, par2=default_list, par3=verschachteltes_default_dict):
mit par0 einem x-beliebigen Argument entsprechend, z. B. ein String (weil der in der Auflistung fehlt ;-) ).
Da muss man sich halt entsprechende Notationen einfallen lassen.
Außerdem, und hier wird es wichtig, wie sieht es mit Dekoratoren in Python 3k aus? Ich habe gesucht und nichts gefunden, vermute aber, daß ich bloß zu blöd zum finden war ...
Es wird sie weiterhin in der gewohnten Form geben, zusätzlich auch als Klassendekoratoren.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Antworten