Seite 1 von 1
Return-Type von Dekoratoren
Verfasst: Dienstag 24. Juli 2018, 17:39
von sprudel
Hallo,
ich frage mich gerade, ob es irgendwie möglich ist, den Return-Type von einem Dekorator zu definieren.
Konkret geht es darum, dass ich eine Funktion mit weiterer Funktionalität erweitern möchte - die Funktion selbst soll in einer Klasse gewrappt werden und dann, für die einfachen Fälle einfach mit __call__ ausgeführt werden können. Ich möchte aber weiterhin auch, wenn ich beispielsweise eine Funktion f annotiert habe, für diese die Codevervollständigung von der Klasse, mit der ich sie dekoriert habe, bekommen. So etwas Ähnliches funktioniert ja auch mit @property. Hat da jemand eine Idee? Vielen Dank.
Re: Return-Type von Dekoratoren
Verfasst: Dienstag 24. Juli 2018, 19:22
von kbr
Der Return-Type eines Dekorators ist immer ein callable, egal ob Funktions- oder Klassen-basiert. Bei dem Rest geht mir einiges durcheinander.
Re: Return-Type von Dekoratoren
Verfasst: Dienstag 24. Juli 2018, 20:09
von ThomasL
kbr hat geschrieben: Dienstag 24. Juli 2018, 19:22
Der Return-Type eines Dekorators ist immer ein callable
nicht zwangsläufig, man kann auch einen Decorator so definieren:
Code: Alles auswählen
def black_hole(func):
return None
@black_hole
def irgendwas():
print("Hello")
man bekommt natürlich dann einen TypeError: 'NoneType' object is not callable zur Runtime
Re: Return-Type von Dekoratoren
Verfasst: Dienstag 24. Juli 2018, 21:59
von sprudel
ThomasL hat geschrieben: Dienstag 24. Juli 2018, 20:09
kbr hat geschrieben: Dienstag 24. Juli 2018, 19:22
Der Return-Type eines Dekorators ist immer ein callable
nicht zwangsläufig, man kann auch einen Decorator so definieren:
Code: Alles auswählen
def black_hole(func):
return None
@black_hole
def irgendwas():
print("Hello")
man bekommt natürlich dann einen TypeError: 'NoneType' object is not callable zur Runtime
Das Problem hier ist aber immer noch, dass die Typinferenz irgendwas dann für ein simples Callable hält. Da helfen auch keine Typannotationen von black_hole. Und das ist mein Problem. Gibt es da irgendeinen Trick?
Re: Return-Type von Dekoratoren
Verfasst: Dienstag 24. Juli 2018, 22:13
von __blackjack__
@ThomasL: Es muss kein „callable“ sein, aber wenn man kein „callable“ zurückgibt, dann bekommt man grundsätzlich eine Ausnahme‽ Äh… Es *muss* ein „callable“ sein. Sonst ist's kaputt.
@sprudel: Schau Dir mal das `decorator`-Modul von Michele Simionato an. Dann braucht man keine Typannotation selber schreiben um die Signatur der dekorierten Objekte zu ”retten”.
Re: Return-Type von Dekoratoren
Verfasst: Mittwoch 25. Juli 2018, 02:52
von __deets__
Ich benutze zum retten functools.wraps, AFAIK macht das Michele’s Modul überflüssig.
Re: Return-Type von Dekoratoren
Verfasst: Mittwoch 25. Juli 2018, 06:08
von ThomasL
__blackjack__ hat geschrieben: Dienstag 24. Juli 2018, 22:13
@ThomasL: Es muss kein „callable“ sein, aber wenn man kein „callable“ zurückgibt, dann bekommt man grundsätzlich eine Ausnahme‽ Äh… Es *muss* ein „callable“ sein. Sonst ist's kaputt.
Das wollte ich @kbr auf nette Weise so sagen. Mein Post war nicht unbedingt als konstruktiv zu verstehen, eher als Anekdote.
Re: Return-Type von Dekoratoren
Verfasst: Mittwoch 25. Juli 2018, 08:37
von kbr
@ThomasL: Danke, es auf die nette Weise anzugehen. Natürlich kann ein callable alles zurückgeben. Im Falle von Dekoratoren ergibt es aber keinen Sinn, etwas anderes als ein callable zurück zu liefern. Von dem Kontext ging ich implizit aus – aber bekanntlich ist ja 'explicit better than implicit'.
@__deets__: tatsächlich, wraps zieht auch __annotations__ nach. Hatte ich noch nicht ausprobiert, da ich type hints bislang vermeide. Ist aber folgerichtig, wenn sie denn schon zum Sprachumfang gehören.
Re: Return-Type von Dekoratoren
Verfasst: Mittwoch 25. Juli 2018, 09:03
von __blackjack__
@__deets__: Rettet das mittlerweile tatsächlich die Signatur der dekorierten Funktion? Die Dokumentation von `decorator` suggeriert etwas anderes und auch die Python-Dokumentation spricht nur von Attributen. Hier bräuchte man dann also wirklich Typannotationen im geretteten `__annotations__`-Attribut.
Hm, habe es gerade mal mit Python 3.5 ausprobiert. `inspect.getargspec()` liefert das falsche Ergebnis, dafür gibt es wohl in Python 3 neu `inspect.signature()` was das richtige Ergebnis liefert. Also ich kann bei Python 2.7 nicht auf `decorator` verzichten. Da ich IPython benutze ist es sowieso installiert.

Re: Return-Type von Dekoratoren
Verfasst: Mittwoch 25. Juli 2018, 10:16
von __deets__
Ich muss gestehen ich habe mir das dermaßen ewig her angewöhnt das zu benutzen, dass die Semantik mir nicht mehr geläufig ist - es hat nur die decorator Notwendigkeit entfernt.
Jetzt sehe ich es transportiert nur Namen & Docstring. Das hat aller Wahrscheinlichkeit dann für mich immer gereicht, über Argument Namen und defaults musste ich nie was erfahren.
Re: Return-Type von Dekoratoren
Verfasst: Mittwoch 25. Juli 2018, 10:30
von __blackjack__
@__deets__: Das ist bei Autovervollständigung wie beim OP interessant und ich hatte mir das hauptsächlich wegen generierter Dokumentation angewöhnt. Vielleicht ist Sphinx da mittlerweile auch schlauer, aber damit hatte ich halt mal das Problem das die Signatur in der Dokumentation dann falsch war.
Re: Return-Type von Dekoratoren
Verfasst: Mittwoch 25. Juli 2018, 12:43
von kbr
Hier mal ein kleiner Test (Python 3.6):
Code: Alles auswählen
import functools
def hello(f: callable) -> callable:
"""The hello decorator"""
@functools.wraps(f)
def wrap(wrap_name: str, option:int=42) -> int:
"""The inner wrapper, with unused option and wrong return type"""
return 'Hello, ' + f(wrap_name)
return wrap
@hello
def func(func_name: str, useless:str='default') -> str:
"""Returns a greeting string."""
return f'greetings, stranger: {func_name}!'
for each in ('name', 'doc', 'annotations', 'defaults'):
print('{}: {}'.format(each, getattr(func, '__{}__'.format(each))))
Code: Alles auswählen
name: func
doc: Returns a greeting string.
annotations: {'func_name': <class 'str'>, 'useless': <class 'str'>, 'return': <class 'str'>}
defaults: (42,)
functools.wraps übernimmt nicht die defaults.
Was auch richtig ist.
Re: Return-Type von Dekoratoren
Verfasst: Mittwoch 25. Juli 2018, 14:42
von Sirius3
@kbr: die defaults sind auch eine Eigenschaft der dekorierenden Funktion. Name und DocString kann man ohne Gefahr ändern, was ja auch gemacht wird. Die Annotations zu übertragen kann aber schon falsch sein, wenn sich die Parameter ändern, wie in diesem Fall. Dafür kennt `wraps` das Argument `assigned`, das Defaultmäßig diese Metadaten kopiert: '__module__', '__name__', '__qualname__', '__doc__', '__annotations__', wobei in Deinem Fall alles bis auf __annotations__ kopiert werden darf.
Re: Return-Type von Dekoratoren
Verfasst: Mittwoch 25. Juli 2018, 15:16
von kbr
@Sirius3: Stimmt, die __annotations__ sollten genauso wenig wie die __defaults__ kopiert werden, wenn der Dekorator die Signatur ändert. Wobei es sicher sehr überraschend sein kann, wenn eine dekorierte Funktion nicht mehr der Implementierung gemäß aufzurufen ist. Kann man sicherlich machen, aber ich halte das für fragwürdig - oder es braucht sehr gute Gründe, die dann auch an dekorierter Stelle vermerkt sein sollten.