Seite 1 von 1

Funktionen mit und ohne Klammern aufrufen

Verfasst: Freitag 22. Mai 2015, 12:39
von chevallier
Hallo,

wieso führt beides zum gleichen Ergebnis (man achte auf die Klammern hinter der Funktion, Zeile 3 und 7):

Code: Alles auswählen

def outerdec(func):
	def innerdec():
		return func + 1
	return innerdec()
def normalfunc():
	return 3
newfunc=outerdec(normalfunc())
print(newfunc)
:: UND ::

Code: Alles auswählen

def outerdec(func):
	def innerdec():
		return func() + 1
	return innerdec()
def normalfunc():
	return 3
newfunc=outerdec(normalfunc)
print(newfunc)
Wie kann man logisch an die Sache herangehen? Wieso gehts mal ohne, mal mit Klammern. Ich weiß, das hat etwas damit zu tun, dass Funktionen Objekte sind und man ohne Klammern nur die Objekte aufruft, aber trotzdem blicke ich da nicht durch.

Und wieso kriege ich eine Fehlermeldung wenn ich Versuche den Wrapper am Ende durch
print(newfunc())
aufzurufen (mit Klammern)?

Danke und Grüße,
Chevallier

Re: Funktionen mit und ohne Klammern aufrufen

Verfasst: Freitag 22. Mai 2015, 12:45
von darktrym
Scharf überlegen:

Code: Alles auswählen

outerdec(normalfunc)() 

Re: Funktionen mit und ohne Klammern aufrufen

Verfasst: Freitag 22. Mai 2015, 12:56
von Hyperion
chevallier hat geschrieben: Wie kann man logisch an die Sache herangehen? Wieso gehts mal ohne, mal mit Klammern. Ich weiß, das hat etwas damit zu tun, dass Funktionen Objekte sind und man ohne Klammern nur die Objekte aufruft, aber trotzdem blicke ich da nicht durch.
Im Grunde genommen ist das recht einfach und Du hast es ja schon erkannt: Der Name einer Funktion verweist auch nur auf das Funktionsobjekt, genau wie bei anderen Zuweisungen auch (Nicht durch die [zahl] verwirren lassen - das sind nur Anzeigen der Shell IPython!):

Code: Alles auswählen

In [3]: a = 4

In [4]: b = list()

In [5]: c = lambda x: x ** 2

In [7]: def foo():
   ...:     print("foo wurde aufgerufen")
   ...:

In [8]: d = foo

In [9]: foo
Out[9]: <function __main__.foo>

In [12]: d
Out[12]: <function __main__.foo>
Man spricht auch davon, dass ein Objekt an einen Namen "gebunden" wird. Bei Funktionen passiert das mittels ``def`` auf spezielle Art und Weise. Man kann das aber auch explizit erledigen, wie eben in [8] gezeigt, wo das Funktionsobjekt zusätzlich zum Namen ``foo`` auch an den Namen ``d`` gebunden wird.

Will man eine Funktion (oder Methode oder allgemein: *Callable*) ausführen, so teilt man das dem Interpreter mit, indem man ein rundes Klammernpaar hinter den Namen setzt:

Code: Alles auswählen

In [10]: c(3)
Out[10]: 9

In [11]: d()
foo wurde aufgerufen
Will man ein Funktions*objekt* als Parameter übergeben, so darf man es *nicht* aufrufen, sondern muss den Namen übergeben:

Code: Alles auswählen

In [13]: from operator import add, sub

In [15]: def operate(func, a, b):
   ....:     return func(a, b)
   ....:

In [16]: operate(add, 2, 1)
Out[16]: 3

In [17]: operate(sub, 2, 1)
Out[17]: 1
So einfach ist das und so ist das auch in Deinen Beispielen. Geh die mal *genau* durch, dann siehst Du, was passiert.

Eine Funktion erzeugen diese beiden Varianten nämlich *nicht* als Rückgabewert, weil jeweils an falscher Stelle der Funktionsaufruf erfolgt!

Re: Funktionen mit und ohne Klammern aufrufen

Verfasst: Freitag 22. Mai 2015, 13:28
von cofi
chevallier hat geschrieben:Und wieso kriege ich eine Fehlermeldung wenn ich Versuche den Wrapper am Ende durch
print(newfunc())
aufzurufen (mit Klammern)?
Weil es noch niemand explizit gesagt hat: `outerdec(normalfunc())` verletzt die Annamen von `outerdec`, naemlich, dass es ein Funktionsobjekt bekommt. Den Fehler siehst du erst beim aufruf von `newfunc`, weil bis dahin nicht die Annahme ueberprueft wird.

In Zukunft bitte auch gleich die Fehlermeldung mitposten. Auch wenn du (noch) nicht viel mit dem Fehler anfangen kannst, dass man Zahlen nicht wie eine Funktion aufrufen kann, koennen es doch andere ;)

Re: Funktionen mit und ohne Klammern aufrufen

Verfasst: Freitag 22. Mai 2015, 13:50
von chevallier
@ Hyperion
Sehr gut erklärt, VIELEN DANK.
:idea: Die Frage, die man sich bei Funktions-Objekten stellen muss lautet:
Übergeben oder ausführen?

Dementsprechend muss der Code so aussehen:

Code: Alles auswählen

def outerdec(func):
	def innerdec():
		return func() + 1		# ausführen
	return innerdec			# übergeben
def normalfunc():
	return 3
newfunc=outerdec(normalfunc)	# übergeben
print(newfunc())				# ausführen
cofi hat geschrieben:In Zukunft bitte auch gleich die Fehlermeldung mitposten. Auch wenn du (noch) nicht viel mit dem Fehler anfangen kannst, dass man Zahlen nicht wie eine Funktion aufrufen kann, koennen es doch andere ;)
Wird gemacht.

Re: Funktionen mit und ohne Klammern aufrufen

Verfasst: Freitag 22. Mai 2015, 13:57
von Hyperion
chevallier hat geschrieben: :idea: Die Frage, die man sich bei Funktions-Objekten stellen muss lautet: Übergeben oder ausführen?
Ich würde sagen zusammen mit dem Löschen sind das einfach die Optionen, die man mit Callables allgemein hat ;-)

Und nun kommen wir noch mal zur Decorator-Syntax:

Code: Alles auswählen

@outerdec
def answer():
    return 42

answer()
> 43
Der Dekoratorausdruck (über der Funktionsdefinition) entspricht also Deiner Zeile 7:

Code: Alles auswählen

newfunc = outerdec(normalfunc)
Es ist nur "syntaktischer Zucker" - nichts anderes :-)

@cofi: Jetzt hatte ich so viel geschrieben, dass mir die Frage zum Fehler vollkommen entgangen ist :mrgreen: Gut dass Du aufgepasst hast!