Dekorator verschluckt Return-Wert

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
gei
User
Beiträge: 3
Registriert: Mittwoch 11. Januar 2017, 10:37

Hallo zusammen,
ich habe im WEB eine schönes Python-Beispiel für eine Debug-Hilfe mittels Dekorator gefunden. Leider verschluckt er den Return-Wert der überwachten Function. Weder im WEB noch durch angestrengtes Überlegen habe ich eine Lösung dafür gefunden.
Hier der Code:

Code: Alles auswählen

import sys

def logged(stream):
	def _inner(fun):
		def _wrapper(*args, **kwargs):
			stream.write('Function %s entry\n' % fun.__name__)
			fun(*args, **kwargs)
			stream.write('Function %s exit\n' % fun.__name__)
		return _wrapper
	return _inner


@logged(stream=sys.stdout)
def foo():
	print 'Doing something'
	return 'foo return'

#---
print foo()

# Die Ausgabe:
# 	Function foo entry
# 	Doing something
# 	Function foo exit
# 	None              <= !!!
Weiß jemand eine Lösung? Für Hilfe wäre ich sehr dankbar!
vg
BlackJack

@gei: Die Lösung ist es den Rückgabewert zurückzugeben. Schau wo die dekorierte Funktion aufgerufen wird und sorg dafür das dort der Rückgabewert an einen Namen gebunden und am Ende dann auch vom ”Wrapper” zurückgegeben wird.
gei
User
Beiträge: 3
Registriert: Mittwoch 11. Januar 2017, 10:37

@BlackJack: zunächst mal vielen Dank für die extrem schnelle Antwort!
Leider fehlt mir die Intelligenz sie auch zu verstehen. Vielleicht habe ich auch Probleme mit der Bezeichnung. Nach meinem Verständnis ist die 'dekorierte Funktion' = 'foo'. Aufgerufen wird sie in Zeile 19, dort gibt sie aber nichts zurück weshalb ich dort auch nichts mit dem Rückgabewert machen kann.
Inzwischen (reichlich spät) ist mir gekommen, dass man den Rückgabewert aus fun() in Zeile 7 weitergeben müßte, aber wie? Außerdem gibt fun() dort auch nichts zurück.
Wenn es nicht zu mühsam ist, hätte ich die Bitte dass du meinen Code entsprechend aufbohrst. Wenn ich den Code dann sehe, kann ich es vielleicht auch verstehen.
Vielen Dank
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Binde das Ergebnis von dem Aufruf der Funktion an einen Zwischenwert. Diesen gibst du am Ende der innersten Funktion zurück. Wie bei jeder anderen Funktion auch. Entweder du stehst gerade auf dem Schlauch oder dir fehlt das Grundlagenwissen.

Übrigens würde ich Wrapper und Inner genau umgekehrt benennen.
BlackJack

@gei: Der Aufruf in Zeile 7 gibt etwas zurück wenn `fun` etwas zurück gibt. Ob/was eine Funktion zurückgibt entscheidet ja nicht der Aufrufer, sondern die Funktion selbst. Der Aufrufer kann nur entscheiden was er damit macht, ob er es ignoriert, wie in Zeile 7 oder ob er etwas damit macht. Zum Beispiel an einen Namen binden und später selbst an *seinen* Aufrufer zurückgeben.

Randbemerkung: Streng genommen kann eine Funktion (oder jede beliebige aufrufbare Objekt) in Python nicht entscheiden *ob* sie etwas zurückgibt, denn in Python hat *jede* Funktion einen Rückgabewert. Wenn nicht explizit etwas mit ``return`` zurückgegeben wird, dann wird implizit `None` zurückgegeben. Dieses `None` hast Du ja bereits gesehen weil die Funktion die `fun` aufruft, ”nichts zurück gibt”.
gei
User
Beiträge: 3
Registriert: Mittwoch 11. Januar 2017, 10:37

Danke, es funktioniert und ich habe es auch begriffen. Ich hatte es so schon mal versucht, erfolglos, hatte da wohl noch einen anderen Fehler gemacht.
Und nochmal vielen Dank für die sehr schnelle Hilfe!
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Du musst dir nur merken, dass in der innersten Funktion die eigentiche Arbeit passiert. Ihre Funktionssignatur nimmt die Argumente der später zu dekorierenden Funktion an und sie ersetzt sozusagen die dekorierte Funktion. Das äußere Gedöns ist aus syntaktischen Gründen nötig, damit die Funktion als Dekorator verwendet werden kann.

Übrigens möchtest du dir hierzu vielleicht functools.wraps() ansehen, welches die Anwendung eines Dekorators noch ein bißchen verbessert. Damit bleiben die Metainformationen der dekorierten Funktion (z.B. Docstring und __name__) erhalten.
Antworten