2015-05-12 28 views
26
def sub3(n): 
    return n - 3 

def square(n): 
    return n * n 

Es ist tot einfach Funktionen in Python zu komponieren:Wie multipliziert man Funktionen in Python?

>>> my_list 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
>>> [square(sub3(n)) for n in my_list] 
[9, 4, 1, 0, 1, 4, 9, 16, 25, 36] 

Leider, wenn die Zusammensetzung als Schlüssel verwenden will, es ist irgendwie lahm:

>>> sorted(my_list, key=lambda n: square(sub3(n))) 
[3, 2, 4, 1, 5, 0, 6, 7, 8, 9] 

Dies sollte wirklich sei einfach sorted(my_list, key=square*sub3), denn verdammt, Funktion __mul__ wird ohnehin für nichts anderes verwendet:

>>> square * sub3 
TypeError: unsupported operand type(s) for *: 'function' and 'function' 

Nun lassen Sie uns es dann definieren!

>>> type(sub3).__mul__ = 'something' 
TypeError: can't set attributes of built-in/extension type 'function' 

D'oh!

>>> class CoolerFunction(types.FunctionType): 
...  pass 
... 
TypeError: Error when calling the metaclass bases 
    type 'function' is not an acceptable base type 

D'oh!

class Hack(object): 
    def __init__(self, function): 
     self.function = function 
    def __call__(self, *args, **kwargs): 
     return self.function(*args, **kwargs) 
    def __mul__(self, other): 
     def hack(*args, **kwargs): 
      return self.function(other(*args, **kwargs)) 
     return Hack(hack) 

Hey, jetzt irgendwo ..

>>> square = Hack(square) 
>>> sub3 = Hack(sub3) 
>>> [square(sub3(n)) for n in my_list] 
[9, 4, 1, 0, 1, 4, 9, 16, 25, 36] 
>>> [(square*sub3)(n) for n in my_list] 
[9, 4, 1, 0, 1, 4, 9, 16, 25, 36] 
>>> sorted(my_list, key=square*sub3) 
[3, 2, 4, 1, 5, 0, 6, 7, 8, 9] 

Aber ich habe keine Hack aufrufbare Klasse wollen wir bekommen! Die Scoping-Regeln sind völlig anders, was ich nicht vollständig verstehe, und das ist sogar noch hässlicher als das "Lameda". Ich möchte die Funktionen monkeypatch. Wie kann ich das machen?

+0

nie etwas ähnlich wie diese gesehen haben, haben Sie versucht, mit 'partials' stattdessen ähnlich wie' Hack' aber vielleicht marginal besser – dashesy

+0

Warum nicht: 'key = Lambda n: Quadrat (n) * sub3 (n)' –

+4

@MalikBrahimi das ist nicht Funktion Zusammensetzung, die ist was Wim will. http: //en.wikipedia.org/wiki/Function_composition –

Antwort

17

Sie können Ihre Hack-Klasse als Dekorateur benutzen, so wie sie geschrieben wurde, obwohl Sie wahrscheinlich einen passenderen Namen für die Klasse wählen würden.

So:

class Composable(object): 
    def __init__(self, function): 
     self.function = function 
    def __call__(self, *args, **kwargs): 
     return self.function(*args, **kwargs) 
    def __mul__(self, other): 
     @Composable 
     def composed(*args, **kwargs): 
      return self.function(other(*args, **kwargs)) 
     return composed 
    def __rmul__(self, other): 
     @Composable 
     def composed(*args, **kwargs): 
      return other(self.function(*args, **kwargs)) 
     return composed 

Sie können dann Ihre Funktionen dekorieren wie so:

@Composable 
def sub3(n): 
    return n - 3 

@Composable 
def square(n): 
    return n * n 

Und komponieren sie etwa so:

(square * sub3)(n) 

Im Grunde ist es das gleiche, was Sie‘ Ich habe es geschafft, Ihre Hack-Klasse zu benutzen, aber als Dekorateur.

+1

Ordentlich. Ich habe eine kleine Verbesserung vorgenommen, so dass die Komposition jetzt mit allen anderen aufrufbaren Funktionen arbeitet, zum Beispiel '(sub3 * int) (" 10 ") -> 7' und' (str * sub3) (10) -> '7'' – wim

2

Python unterstützt (und wird wahrscheinlich nie) die Funktionszusammensetzung entweder auf syntaktischer Ebene oder als Standardbibliotheksfunktion. Es gibt verschiedene Module von Drittanbietern (z. B. functional), die eine Funktion höherer Ordnung bereitstellen, die die Funktionszusammensetzung implementiert.

2

Vielleicht so etwas wie:

class Composition(object): 
    def __init__(self, *args): 
     self.functions = args 

    def __call__(self, arg): 
     result = arg 
     for f in reversed(self.functions): 
      result = f(result) 

     return result 

Und dann:

sorted(my_list, key=Composition(square, sub3)) 
+1

Warum nicht stattdessen einen Verschluss verwenden? – Veedrac

+0

Schließung ist auch in Ordnung. Ich sehe keinen großen Unterschied zwischen diesen Ansätzen (außer dass man mit der Klasse die Funktionsliste nach dem Erstellen der Komposition ändern kann). –

Verwandte Themen