Currying in Python

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
_Mala_Fide_
User
Beiträge: 53
Registriert: Dienstag 22. Dezember 2015, 19:17

Hallo,

ich sitze jetzt seit ein paar Tagen daran currying zu verstehen. Habe dazu meherere Seiten gelesen und komme nicht so richtig weiter.

1) https://python-course.eu/advanced-pytho ... python.php
^^Diesen Link habe ich als pdf gespeichert und den Text ins Deutsche übersetzt: https://www.file-upload.net/download-14 ... u.pdf.html
2) https://de.acervolima.com/currying-funktion-in-python/

In Wikipedia steht: Currying (vor allem in der Linguistik auch Schönfinkeln) ist die Umwandlung einer Funktion mit mehreren Argumenten in eine Sequenz von Funktionen mit jeweils einem Argument.

Die Beispiele in beiden Links stellen aber ganau das nicht dar. Dort werden mehrere Funktionen erstellt, die aufeinander "aufbauen". Sprich eine Funktion die Rückgabe einer anderen Funktion verwendet, um eine Berechnung auszuführen. Diese Funktionen werden dann einer "Hilfsfunktion" übergeben, die man dann mit einem Wert aufrufen kann und die das Ergebnis aller verketteten Funktionen zurückgibt, so dass man nicht mehrere Funktionen einzeln mit den Rückgabewerten anderer Funktionen aufrufen muss. Dann kommt das letzte Beispiel des ersten Links, was eine Funktion mit beliebigen Argumenten annimmt und bei einer Eingabe von 0 Argumenten das Ergebnis aller vorherigen argumente ausgibt.

Aber was genau ist von all dem currying und was macht currying genau? Kann mir bitte jemand helfen und das ausführlich erklären?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Den Begriff Schönfinkeln kannte ich nicht, es ist sehr bedauerlich, dass der nicht weiter verwendet wird!

Den python-course.eu finde ich ja so mittel. Und das bestaetigt sich IMHO hier wieder. Im Grunde redet er hauptsaechlich von function composition. Das hat aber eigentlich nicht so viel mit currying zu tun. Was man dann daran sieht, dass es beim spaeter erfolgenden wirklichen currying auch nicht mehr gebraucht wird. Warum er das also einfuehrt...

Das richtig currying findet sich unter der Ueberschrift "Currying Function with an Arbitrary Number of Arguments" - da wird das gezeigt. Wenn man eine Funktion f mit Argumenten a, b, c hat, und die Sprache currying unterstuetzt, dann kann man die so aufrufen:

Code: Alles auswählen

f(1)(2)(3)
Python ist keine solche Sprache. Da muss man dann ueblicherweise einen currying-Operator definieren, der einem eben eine solche Funktion liefert.

Code: Alles auswählen

curry(f)(1)(2)(3)
Jetzt klarer?
_Mala_Fide_
User
Beiträge: 53
Registriert: Dienstag 22. Dezember 2015, 19:17

Mhh..., finde diesen Kurs eigentlich bis jetzt ganz gut, obwohl ich schon bei ein paar anderen Tehmen das Internet intensivst befragen musste, da kam mir dann der Kurs viel zu kurz. Finde es auch schade, dass sich der deutsche und der englische Kurs sehr stark unterscheiden. Im Deutschen fehlen viele Kapitel und neuere Dinge gibt es auch nur im Englischen. Und da ich englisch zwar recht gut lesen kann, aber bei vielen Fachbegriffen trotzdem nachschlagen muss, lässt sich das sehr schwer lernen. Daher habe ich kurzerhand den kompletten Kurs als pdf's gespeichert und nach und nach übersetzt, so kann ich den in Ruhe in deutsch durcharbeiten.

Ok, danke für die Erklärung. Duch den Anfang des Kapitel war ich echt durcheinander und wusste nicht genau weiter.
Naja, so langsam dämmert es ein wenig, muss ich aber noch öffter lesen um mir einen Reim daraus machen zu können...
Wozu wird currying denn verwendet? Ich sehe darin gerade keinen Nutzen.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das hat grosse Vorteile zB bei GUI-Programmierung, wo man oft ein Muster hat, bei denen eine Rueckruffunktion angegeben werden muss, zB wenn ein Knopf gedrueckt wird. Und die bekommt dann keine oder vielleicht einen Event-Parameter oder so. Man will aber noch andere Parameter randekorieren, zB den Namen des Buttons oder eine Datenstruktur oder sowas - und mit currying kann man dann ganz einfach sowas machen:

Code: Alles auswählen

def callback(button_id, event):
    print(button_id, event)

button = Button("Drueck mich", command=callback("der_drueck_mich_button") # Achtung! So geht's in Python ja genau nicht!
Statt "richtigem" currying macht man das in Python dann mit functools partial:

Code: Alles auswählen

button = Button("Drueck mich", command=functools.partial(callback, "der_drueck_mich_button")
_Mala_Fide_
User
Beiträge: 53
Registriert: Dienstag 22. Dezember 2015, 19:17

Ich beschäftige mich gerade wieder mit currying und weiß nicht, wie ich partial dort einordnen soll.

Code: Alles auswählen

from functools import partial

def arimean(*args):
    return sum(args) / len(args)

partial_arimean = partial(arimean, *(1, 2, 3))
print(partial_arimean(4,5,6))
^^So kann ich partial_arimean verwenden.

Code: Alles auswählen

print(partial_arimean(4)(5)(6))
^^So aber nicht, dann kommt folgende Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "./O_o.py", line 61, in <module>
    print(partial_arimean(4)(5)(6))
TypeError: 'float' object is not callable
Wenn ich das richtig verstehe, hat partial nichts mit currying zu tun, sondern eher mit closure?

Code: Alles auswählen

def partial_test(func, *args1):
    def helper(*args2):
        return func(*args1, *args2)
    return helper

test_arimean = partial_test(arimean, *(1, 2, 3))
print(test_arimean(4,5,6))
^^Dies hat das gleiche Verhalten wie ich oben mit partial erzeugt habe.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich schrieb ja, partial ist nicht richtiges currying. Es ist aber in vielen Fällen beim Einsatz gleich. Denn warum will man denn überhaupt currying? Das macht ja nur Sinn, wenn man eine teilangewandte Funktion sinnvoll nutzen kann. Und das kann man dann eben in Python mit partial ebenso erreichen. Es ist nicht gleich, aber oft gleichwertig.

Als Implementierung für partial *kann* ein closure zum Einsatz kommen. Muss aber nicht. Und tut es AFAIK auch nicht, das sind spezielle partial Objekte.
Benutzeravatar
__blackjack__
User
Beiträge: 14078
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@_Mala_Fide_: „Eher mit closure“ ist irreführend denn currying erzeugt ja auch closures. Currying wäre quasi `partial()` für jedes einzelne Positions-Argument explizit aufzurufen, nur dass das bei currying durch ”Magie” passiert. Zum Beispiel in Haskell weil das direkt in der Programmiersprache passiert, oder weil man sich in anderen Sprachen da irgendwas gebastelt hat, das die Argumente sammelt, und feststellen kann, wann die komplett sind und dann letztlich den Aufruf macht.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
_Mala_Fide_
User
Beiträge: 53
Registriert: Dienstag 22. Dezember 2015, 19:17

Achso, ok. Ich kann mir nur vorstellen, dass echtes currying nur Sinnvoll ist, wenn eine Funktion öffter mit anderen Werten aufgerufen wird, die Funktion aber erst das Ergebnis zurückgeben soll, wenn alle Aufrufe abgearbeitet sind.
Obwohl ich die Werte der Aufrufe warscheinlich erst in einer Liste speichern würde und dann mit den Werten der Liste die Funktion aufrufen würde...
Danke nochmal für deine Hilfe.
_Mala_Fide_
User
Beiträge: 53
Registriert: Dienstag 22. Dezember 2015, 19:17

@__blackjack__
Ja da hast Du recht, da habe ich mich etwas falsch ausgedrückt. Wofür würdest Du denn sagen, ist currying in python zu gebrauchen?
Benutzeravatar
__blackjack__
User
Beiträge: 14078
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@_Mala_Fide_: Im Grunde gar nicht, weil es `functools.partial()` gibt. Currying ist eine etwas allgemeinere Variante davon. Allerdings kann currying `partial()` nicht in allen Fällen ersetzen, beispielsweise wenn man tatsächlich eine Funktion haben möchte bei der alle Positions-Argumente gebunden werden und die dann ohne Argumente aufgerufen werden soll. Es sei denn man macht den letztlichen Aufruf auch noch mal explizit. Funktionen die eine beliebige Anzahl von Argumenten entgegen nehmen, wären auch ein Problem.

Es gibt da draussen mehrere Implementierungen beispielsweise von `curry`-Dekoratoren. Wenn man das tatsächlich in Python haben möchte, kann man es relativ einfach implementieren oder aus einer Bibliothek nachinstallieren.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Antworten