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

def square(n): 
    return n * n 

E 'morto facile comporre le funzioni in Python:Come moltiplicare le funzioni 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] 

Purtroppo, quando si vuole usare la composizione come una chiave , è una specie di lame:

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

Questo dovrebbe davvero solo essere sorted(my_list, key=square*sub3), perché diamine, la funzione __mul__ non viene comunque utilizzata per altri scopi:

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

Bene, allora definiamolo!

>>> 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, ora stiamo ottenendo da qualche parte ..

>>> 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] 

Ma io non voglio una classe callable Hack! Le regole di scoping sono totalmente diverse in modi che non comprendo appieno, e questo è anche più brutto della "lameda". Voglio monkeypatch le funzioni . Come lo posso fare?

+0

non hanno mai visto nulla di simile a questo, Hai provato a usare 'partials' invece, simile a' Hack' ma forse marginalmente meglio – dashesy

+0

Perché no: 'key = lambda n: square (n) * sub3 (n)' –

+4

@MalikBrahimi che non è la composizione della funzione, che è ciò che vuole wim. http: //en.wikipedia.org/wiki/Function_composition –

risposta

17

Puoi usare la tua classe di hack come decoratore praticamente come è scritto, anche se probabilmente vorrai scegliere un nome più appropriato per la classe.

Ti piace questa:

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 

è quindi possibile decorare le funzioni in questo modo:

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

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

E comporli in questo modo:

(square * sub3)(n) 

in fondo è la stessa cosa che si' ho realizzato usando la tua classe di hacking, ma usandola come decoratore.

+1

Neat. Ho apportato un leggero miglioramento, quindi ora la composizione funziona con qualsiasi altro callable ad esempio '(sub3 * int) (" 10 ") -> 7' e' (str * sub3) (10) -> '7'' – wim

2

Python non ha (e probabilmente non avrà mai) supporto per la composizione delle funzioni a livello sintattico o come funzione di libreria standard. Esistono vari moduli di terze parti (ad esempio functional) che forniscono una funzione di ordine superiore che implementa la composizione delle funzioni.

2

Forse qualcosa di simile:

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 

E poi:

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

Perché non usare una chiusura invece? – Veedrac

+0

Anche la chiusura va bene. Non vedo molta differenza tra questi approcci (tranne che con la classe è possibile modificare l'elenco delle funzioni dopo la creazione della composizione). –

Problemi correlati