typechecking decorator für python 2.x

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Sirius3
User
Beiträge: 17748
Registriert: Sonntag 21. Oktober 2012, 17:20

Mein erster Gedanke beim Lesen war auch, »warum muß ich jetzt die Parameter in einem Dictionary angeben?«. Für normale Positionsargumente nimmt man eben None oder True oder False oder ANY um anzuzeigen, dass der Typ egal ist, bzw. man kann, wenn man lieber mit Parameternamen arbeitet auch Keywordargumente benutzen.
Jetzt bin ich noch auf die ganz verwegene Idee gekommen, warum eigentlich nicht noch Funktionen zulassen, die mit denen man beliebige Bedingungen an die Parameter stellen kann:

Code: Alles auswählen

@checkparams(lambda param_a: 0<=param_a<=10, None, str)
def func(param_a, param_b, param_c):
    do_sth
lunar

@Sirius3 Das lässt sich nicht so ohne weiteres implementieren, weil man nicht zwangsläufig unterscheiden kann zwischen einem „normalen“ aufrufbaren Objekt, sprich einer Funktion, und einem aufrufbaren Objekt, dass einen vererbbaren Typen darstellt. Vergiss nicht, dass Klassen auch aufrufbare Objekte sind.
Sirius3
User
Beiträge: 17748
Registriert: Sonntag 21. Oktober 2012, 17:20

@lunar: die Unterscheidung ist relativ einfach:
a) alles was eine __base__ hat, ist ein Typ und kann mit isinstance geprüft werden.
b) alles was __call__-able ist, wird mit dem Parameter aufgerufen und auf Wahrheit geprüft.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Sirius3 hat geschrieben:a) alles was eine __base__ hat, ist ein Typ und kann mit isinstance geprüft werden.
Wobei man damit (in Python 2) oldstyle-Klassen ausschließen würde:

Code: Alles auswählen

>>> class Foo: pass
... 
>>> Foo.__base__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: class Foo has no attribute '__base__'
>>> class Bar(object): pass
... 
>>> Bar.__base__
<type 'object'>
Wenn schon, würde ich lieber inspect.isclass verwenden.
BlackJack

@Sirius3: Aber damit schliesst man doch Exemplare aus die zwar `__base__` haben, aber auch eine `__call__`-Implementierung die zum Testen gedacht ist. Zumal ich nicht sehe dass die Sprachdefinition verbietet das Funktionsobjekte `__base__` haben dürfen. Das heisst das könnten zukünftige Versionen durchaus haben, oder auch alternative Implementierungen zu CPython.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

BlackJack hat geschrieben:@Sirius3: Aber damit schliesst man doch Exemplare aus die zwar `__base__` haben, aber auch eine `__call__`-Implementierung die zum Testen gedacht ist.
Ich denke mal, es war so gemeint, dass man zunächst auf `__base__` testen soll und nur in dem Fall, wenn dieses Attribut nicht vorhanden ist, das Argument als Callback-Funktion aufgefasst wird und erst dann der Test auf Aufrufbarkeit folgen würde. Letzteres könnte man natürlich genau so gut erreichen, wenn man nach einem negativen `__base__`-Test einfach den Aufruf probiert und dann schaut, ob ein Fehler geworfen wird oder nicht.

Insgesamt erscheint mir das aber sowieso ein bißchen zu magisch. Sicherlich lässt sich auch ein anderer Weg finden, um eigene Prüfer übergeben zu können. Type-Checks sind ja doch ein bißchen was anderes als die Validierung von Werten. Das könnte man bei Gefallen IMHO eher separat einbauen, falls man denn ein Validierung-Framework haben möchte.
Sirius3
User
Beiträge: 17748
Registriert: Sonntag 21. Oktober 2012, 17:20

ich sehe den generellen Unterschied zwischen Typ- und sonstiger Validierung nicht.
Die einfachste Lösung ist wohl, ganz auf Typvalidierung zu verzichten und es extern zu lösen:

Code: Alles auswählen

def check_type(typ):
    return lambda param: isinstance(param, typ)

@checkparams(lambda param_a: 0<=param_a<=10, None, check_type(str))
def func(param_a, param_b, param_c):
    do_sth
lunar

@Sirius3 Du siehst wohl auch nicht die Auswirkung Deines Vorschlags auf die API eines solchen Dekorators:

Code: Alles auswählen

@checkparams(check_type(str), check_type(int), cookit=check_type(bool)).returns(check_type(bool))
def foo(spam, eggs, cookit=False):
    return True
Hübsch-hässlich, was?

Zudem trennt Dein Vorschlag nicht zwischen den unterschiedlichen Operationen Typprüfung und Wertprüfung, und verletzt mithin das „Separation of Concerns“-Prinzip: Eine Aufgabe, eine Funktion. Im Übrigen ist ein eigener Dekorator zur Wertprüfung schon deswegen nötig, weil Python für Typ- und Wertfehler die unterschiedlichen Ausnahmen "TypeError" beziehungsweise "ValueError" vorsieht.

Code: Alles auswählen

@typesafe(str, int, cookit=bool).returns(int)
@validate(None, lambda x: x <= 10).returns(lambda x: x>= 50)
def foo(spam, eggs, cookit=False):
    return 100
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Und von einem ausgereiften Validierungsframework würde ich auch eher erwarten, dass so etwas wie `less_or_equals(10)` anstatt `lambda x: x <= 10` möglich ist. Die vorgenannte Funktion würde also ihrerseits den eigentlichen Callback zurückliefern und könnte exakt so im Decorator stehen. Eigene Lambda-Funktionen sollten dann wirklich nur als letzte Lösung in Betracht kommen.

Auch verstehe ich nicht so ganz den Sinn, wieso Keyword-Argumente in der Signatur zwangsläufig mittels Keyword-Argumenten bei der Validierung / Typprüfung angesprochen werden sollen. Ich würde mich da eher an die Aufrufkonvention von Python halten und annehmen, dass die Reihenfolge an zu prüfenden Typen exakt der Reihenfolge der Argumente in der Funktionssignatur entspricht. Ein Ansprechen über `argname=typangabe` wäre dann optional bzw vielleicht auch dann sinnvoll, wenn man einige Argumente aus der Prüfung ausnehmen möchte. So könnte man sich die `None`s als Platzhalter sparen.
khz
User
Beiträge: 38
Registriert: Freitag 3. August 2012, 22:47
Kontaktdaten:

@snafu
Auch verstehe ich nicht so ganz den Sinn, wieso Keyword-Argumente in der Signatur zwangsläufig mittels Keyword-Argumenten bei der Validierung / Typprüfung angesprochen werden sollen. Ich würde mich da eher an die Aufrufkonvention von Python halten und annehmen, dass die Reihenfolge an zu prüfenden Typen exakt der Reihenfolge der Argumente in der Funktionssignatur entspricht

Ist es nicht so, das bei keyword-arguments die Reihenfolge eben keine Rolle spielt ??

Dann wuerde man das eben nicht zwangslaeufig annehmen. (muessen und sollen)
http://www.pythononwheels.org
11to1 sparetime development and all pm.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Man kann Funktionen mit Keyword-Argumenten auch wie folgt benutzen:

Code: Alles auswählen

>>> def foo(one=1, two=2):
...     print one, two
... 
>>> foo(42, 23)
42 23
Das meinte ich. Es reicht aus, die Reihenfolge einzuhalten. Man muss nicht zwangsläufig das Keyword-Argument explizit benennen.
khz
User
Beiträge: 38
Registriert: Freitag 3. August 2012, 22:47
Kontaktdaten:

ah, verstehe.
http://www.pythononwheels.org
11to1 sparetime development and all pm.
Antworten