Warum gibt functools.partial keine echte Funktion zurück (und wie erstellt man eine Funktion)?

8

Ich habe also mit Currying-Funktionen in Python herumgespielt, und eines der Dinge, die mir auffielen, war, dass functools.partial ein Teilobjekt anstatt einer eigentlichen Funktion zurückgibt. Eine der Sachen, die mich daran störte, war, dass wenn ich etwas in der Art von:

tat %Vor%

dann bekommen wir

%Vor%

Aber was ich will, ist

%Vor%

Gibt es einen sauberen Weg, um es so zu machen? Ich habe eine Workaround geschrieben, aber es ist zu hacky für meinen Geschmack (funktioniert noch nicht für Funktionen mit varargs):

%Vor%

Bearbeiten: Ich denke, es könnte hilfreich sein, wenn ich mehr Kontext hinzufüge. Ich schreibe einen Dekorateur, der automatisch eine Funktion wie folgt curry:

%Vor%

Ich möchte die Tatsache verbergen, dass f aus einer partiellen (vielleicht eine schlechte Idee: P) entstanden ist. Die Nachricht, die die obige TypeError-Nachricht gibt, ist ein Beispiel dafür, ob etwas, das ein Partial ist, enthüllt werden kann. Ich möchte das ändern.

Dies muss verallgemeinerbar sein, so dass die Vorschläge von EnricoGiampieri und mgilson nur in diesem speziellen Fall funktionieren.

    
epsilon 20.11.2012, 22:47
quelle

2 Antworten

4

Das willst du definitiv nicht mit exec machen.

Sie können Rezepte für partial in reinem Python finden, zB dieser - viele von ihnen sind falsch als curry Rezepte, also auch darauf achten. Auf jeden Fall zeigen diese Ihnen den richtigen Weg, um es ohne exec zu tun, und Sie können nur einen auswählen und ändern, um zu tun, was Sie wollen.

Oder Sie könnten einfach partial ... umbrechen.

Was auch immer Sie tun, es gibt keine Möglichkeit, wie der Wrapper wissen kann, dass er eine Funktion namens "five" definiert; das ist nur der Name der Variablen, in der Sie die Funktion speichern. Wenn Sie also einen benutzerdefinierten Namen haben wollen, müssen Sie ihn an die Funktion übergeben:

%Vor%

An diesem Punkt müssen Sie sich fragen, warum das besser ist, als nur eine neue Funktion zu definieren.

Aber ich glaube nicht, dass Sie das wirklich wollen. Ihr ultimatives Ziel ist es, einen @curry Decorator zu definieren, der eine Curry-Version der Decorated-Funktion mit demselben Namen (und Docstring, Arg-Liste usw.) als dekorierte Funktion erstellt. Die ganze Idee, den Namen des Zwischenprodukts partial zu ersetzen, ist ein Ablenkungsmanöver; Verwenden Sie functools.wraps ordnungsgemäß in Ihrer curry -Funktion, und das spielt keine Rolle Wie Sie die Curry-Funktion definieren, wird der Name des Originals beibehalten.

In einigen Fällen funktioniert functools.wraps nicht. Und tatsächlich kann dies einer dieser Zeiten sein - Sie müssen zum Beispiel die Arg-Liste ändern, so dass curry(len) entweder 0 oder 1 Parameter annehmen kann, anstatt 1 Parameter zu benötigen, richtig? Siehe update_wrapper und das (sehr einfache) Quellcode für wraps und update_wrapper , um zu sehen, wie die Grundlagen funktionieren, und von dort zu bauen.

>

Erweitern des vorherigen: Um eine Funktion zu curren, müssen Sie ziemlich genau etwas zurückgeben, das (*args) oder (*args, **kw) benötigt, und die Argumente explizit parsen und möglicherweise TypeError und andere geeignete Ausnahmen explizit erhöhen. Warum? Nun, wenn foo 3 Params benötigt, nimmt curry(foo) 0, 1, 2 oder 3 Params, und wenn 0-2 Params angegeben werden, gibt es eine Funktion zurück, die 0 bis n-1 Params akzeptiert.

Der Grund, warum Sie **kw möchten, ist, dass Anrufer Params nach Namen angeben können - obwohl es dann viel komplizierter wird, sie zu überprüfen, wenn Sie mit dem Akkumulieren von Argumenten fertig sind - Es ist vielleicht besser, zuerst die benannten Parameter mit partial , dann curry das Ergebnis zu binden und alle übrigen Parameter im Curry-Stil zu übergeben ...

Wenn foo default-value oder keyword args hat, wird es noch komplizierter, aber selbst ohne diese Probleme müssen Sie sich bereits mit diesem Problem befassen.

Nehmen wir beispielsweise an, Sie implementieren curry als eine Klasse, die die Funktion und alle bereits curierten Parameter als Instanzmitglieder enthält. Dann wirst du so etwas haben:

%Vor%

Das ist schrecklich vereinfacht, aber es zeigt, wie man mit den Grundlagen umgeht.

    
abarnert 20.11.2012, 23:05
quelle
0

Meine Vermutung ist, dass die partielle Funktion nur die Ausführung der Funktion verzögert, und nicht eine ganz neue Funktion daraus erzeugt.

Meine Vermutung ist, dass es einfacher ist, direkt eine neue Funktion zu definieren:

%Vor%

Dies ist eine sehr einfache Zeile, die den Code nicht durcheinander bringt und ziemlich klar ist, also würde ich keine Mühe haben, eine Funktion zu schreiben, um sie zu ersetzen, besonders wenn Sie diese Situation nicht in einer großen Anzahl von Fällen benötigen / p>     

EnricoGiampieri 20.11.2012 22:57
quelle

Tags und Links