2010-02-17 7 views
18

Vor einiger Zeit habe ich über Haskell docs geschaut und festgestellt, dass es ein funktionaler Kompositionsoperator ist. Also habe ich diese kleine Dekorateur umgesetzt:Ist es eine gute Idee, eine Syntax Sugar zu haben, um die Komposition in Python zu funktionieren?

from functools import partial 

class _compfunc(partial): 
    def __lshift__(self, y): 
     f = lambda *args, **kwargs: self.func(y(*args, **kwargs)) 
     return _compfunc(f) 

    def __rshift__(self, y): 
     f = lambda *args, **kwargs: y(self.func(*args, **kwargs)) 
     return _compfunc(f) 

def composable(f): 
    return _compfunc(f) 

@composable  
def f1(x): 
    return x * 2 

@composable 
def f2(x): 
    return x + 3 

@composable 
def f3(x): 
    return (-1) * x 

@composable 
def f4(a): 
    return a + [0] 

print (f1 >> f2 >> f3)(3) #-9 
print (f4 >> f1)([1, 2]) #[1, 2, 0, 1, 2, 0] 
print (f4 << f1)([1, 2]) #[1, 2, 1, 2, 0] 

Das Problem: ohne Sprachunterstützung wir diese Syntax nicht auf eingebauten Funktionen oder Lambda-Ausdrücke wie diese verwenden:

((lambda x: x + 3) >> abs)(2) 

Die Frage: ist es nützlich? Lohnt es sich, auf der Python-Mail-Liste diskutiert zu werden?

+0

... aber es ist immer noch möglich, etwas wie dieses zu schreiben: drucken (zusammensetzbar (Lambda x: x + 3) >> zusammensetzbar (abs)) (- 4) – si14

Antwort

8

IMHO: Nein, ist es nicht. Während ich Haskell mag, passt das einfach nicht in Python. Anstatt (f1 >> f2 >> f3) können Sie compose(f1, f2, f3) tun, und das löst Ihr Problem - Sie können es mit jeder aufrufbaren verwenden, ohne Überlastung, Dekoration oder Änderung des Kerns (IIRC jemand bereits vorgeschlagen functools.compose mindestens einmal; Ich kann es jetzt nicht finden).

Außerdem ist die Sprachdefinition jetzt eingefroren, so dass sie diese Art von Änderung ohnehin wahrscheinlich ablehnen - siehe PEP 3003.

+0

komponieren() ist nicht so klar. Was ist die Richtung der Komposition? Sie müssen sich die Dokumentation ansehen, um diese Frage zu beantworten. – si14

+2

@ si14: Ich weiß nicht, Haskell >> und << nicht, die entweder klar (bedeutet es einfügen oder wickeln Sie die andere Funktion?). – nikow

+1

@Nikow: Ich weiß es auch nicht, aber >> und << klar definieren den "Rechenfluss": f1 >> f2 >> f3 bedeutet "put Ergebnis von f1 bis f2, dann Ergebnis von f2 bis f3". Alternative ist f1 (f2 (f3 (x))) mit vielen unordentlichen Klammern. – si14

6

Die Funktionszusammensetzung ist in Python keine besonders häufige Operation, insbesondere nicht so, dass ein Kompositionsoperator eindeutig benötigt wird. Wenn etwas hinzugefügt wurde, bin ich mir nicht sicher, ob ich die Wahl von << und >> für Python mag, die für mich nicht so offensichtlich sind, wie sie Ihnen scheinen.

Ich vermute, eine Menge Leute mit einer Funktion bequemer wäre compose, von denen der Auftrag nicht problematisch ist: compose(f, g)(x)f(g(x)) bedeuten würde, die gleiche Reihenfolge wie o in Mathe und . in Haskell. Python versucht, Satzzeichen zu vermeiden, wenn englische Wörter ausreichen, insbesondere wenn die Sonderzeichen keine allgemein bekannte Bedeutung haben. (Ausnahmen sind für Dinge gemacht, die zu nützlich erscheinen, wie @ für Dekorateure (mit viel Zögern) und * und ** für Funktionsargumente.)

Wenn Sie sich entscheiden, dies an Python-Ideen zu senden, Sie werden wahrscheinlich viel mehr Leute gewinnen, wenn Sie einige Instanzen in der stdlib oder in populären Python-Bibliotheken finden können, deren Zusammensetzung den Code klarer, einfacher zu schreiben, wartbar oder effizient gemacht hätte.

+0

Können Sie bitte klarstellen, warum '' '' '' '' '' ''nicht offensichtlich sind? Ich stimme nicht zu, ich versuche nur zu sehen, ob es einen Konsens unter den Programmierern gibt. – max

+0

@max, ich habe keine Ahnung, wie man beschreibt, warum etwas nicht offensichtlich ist. Es gibt einfach keinen Grund, dass ich "<<' and '>>" anschauen und sagen würde: "Ah, sie sollten dafür verwendet werden!" –

+0

@max ein Versuch: es ist * weniger offensichtlich * aufgrund * keine Tradition * dieser Verwendung. Auch * weniger offensichtlich * aufgrund * in einem anderen Sinn * '>>' wird in der erweiterten Druckanweisung ('Chevron drucken') verwendet. Auch * nicht offensichtlich * zu binden [Shifting-Operationen] (https://docs.python.org/2/reference/expressions.html#shifting-operations) von [spezielle Funktionen] (https://docs.python.org/ 2/reference/datamodel.html # emulating-numeric-types) zur Komposition für * all * [callables] (https://docs.python.org/2/reference/datamodel.html#emulating-callable-objects): * es ist unklar, welche Arten von Objekten dies disqualifizieren würde *. – n611x007

6

Sie können es mit reduce, obwohl die Reihenfolge der Anrufe von links nach rechts ist nur:

def f1(a): 
    return a+1 

def f2(a): 
    return a+10 

def f3(a): 
    return a+100 

def call(a,f): 
    return f(a) 


reduce(call, (f1, f2, f3), 5) 
# 5 -> f1 -> f2 -> f3 -> 116 
reduce(call, ((lambda x: x+3), abs), 2) 
# 5 
1

Ich habe nicht genug Erfahrung mit Python haben einen Blick auf, ob eine Sprache ändern würde sich lohnen. Aber ich wollte die verfügbaren Optionen mit der aktuellen Sprache beschreiben.

unerwartetes Verhalten zu schaffen, um zu vermeiden, sollte funktionale Zusammensetzung folgt in idealer Weise der Standard-Mathematik (oder Haskell) Reihenfolge der Operationen, das heißt f ∘ g ∘ h soll h bedeuten gelten, dann g, dann f.

Wenn Sie einen vorhandenen Operator in Python verwenden möchten, sagen Sie <<, wie Sie erwähnen, dass Sie ein Problem mit Lambdas und eingebauten haben. Sie können Ihr Leben leichter machen, indem Sie die reflektierte Version __rlshift__ zusätzlich zu __lshift__ definieren. Damit wären Lambda/Einbauten neben composable Objekten erledigt.Wenn Sie zwei benachbarte Lambda/Einbauteile haben, müssen Sie (nur eine davon) mit composable explizit konvertieren, wie @si14 vorgeschlagen. Hinweis Ich meine wirklich __rlshift__, nicht __rshift__; tatsächlich würde ich davon abraten, überhaupt __rshift__ zu verwenden, da die Bestelländerung trotz des durch die Form des Bedieners bereitgestellten Richtungshinweis verwirrend ist.

Aber es gibt einen anderen Ansatz, den Sie vielleicht in Erwägung ziehen sollten. Ferdinand Jamitzky hat eine great recipe für die Definition von Pseudo-Infix-Operatoren in Python, die sogar auf Built-Ins funktionieren. Mit diesem können Sie f |o| g für Funktionszusammensetzung schreiben, die tatsächlich sehr vernünftig aussieht.