10

Lavoro in Python. Recentemente, ho scoperto un meraviglioso pacchetto chiamato fn. L'ho usato per la composizione delle funzioni.Migliore funzione composizione in Python

Per esempio, invece di:

baz(bar(foo(x)))) 

con fn, è possibile scrivere:

(F() >> foo >> bar >> baz)(x) . 

Quando ho visto questo, ho pensato subito a Clojure:

(-> x foo bar baz) . 

Ma nota come, in Clojure, l'input è a sinistra. Mi chiedo se questo sia possibile in python/fn.

+2

Mentre l'overloading degli operatori comportamento è interessante, a me questo sembra proprio come una cattiva cosa da fare nel codice reale. –

+0

Non c'è modo di far funzionare la sintassi esatta in Python, se è quello che stai chiedendo. Potresti essere in grado di approssimarlo in vari modi. Che cosa è esattamente importante per te in termini di sintassi? – BrenBarn

+0

Mantenere il flusso da sinistra a destra.Attualmente, l'argomento della funzione composta è alla fine. Sarebbe più chiaro se potessi scrivere F() >> x >> foo >> bar >> baz, o simili. –

risposta

9

Non è possibile replicare la sintassi esatta, ma si può fare qualcosa di simile:

def f(*args): 
    result = args[0] 

    for func in args[1:]: 
     result = func(result) 

    return result 

sembra funzionare:

>>> f('a test', reversed, sorted, ''.join) 
' aestt' 
2

Non si può ottenere che sintassi esatta, anche se è possibile ottenere qualcosa come F(x)(foo, bar, baz). Ecco un semplice esempio:

class F(object): 
    def __init__(self, arg): 
     self.arg = arg 

    def __call__(self, *funcs): 
     arg = self.arg 
     for f in funcs: 
      arg = f(arg) 
     return arg 

def a(x): 
    return x+2 
def b(x): 
    return x**2 
def c(x): 
    return 3*x 

>>> F(2)(a, b, c) 
48 
>>> F(2)(c, b, a) 
38 

Questo è un po 'diversa dalla risposta di Blender dal momento che memorizza l'argomento, che può essere successivamente riutilizzato con funzioni diverse.

Questo è un po 'l'opposto della normale applicazione di funzione: invece di specificare la funzione in primo piano e lasciando alcuni argomenti da specificare in seguito, si specifica l'argomento e si lascia la funzione (o le funzioni) da specificare in seguito. È un giocattolo interessante ma è difficile pensare perché lo vuoi davvero.

1

Se si desidera utilizzare fn, con un piccolo hack è possibile ottenere un po 'più vicino a Clojure sintassi:

>>> def r(x): return lambda: x 
>>> (F() >> r(x) >> foo >> bar >> baz)() 

Vedere come ho aggiunto un'altra funzione all'inizio della catena composizione che sarà solo tornare x quando chiamato. Il problema con questo è che devi ancora chiamare la tua funzione composta, solo senza argomenti.

Penso che la risposta di Blender sia la soluzione migliore per provare ad emulare la funzione di thread di Clojure in Python.

0

sono arrivato fino a questo

def _composition(arg, *funcs_and_args): 
    """ 
    _composition(
     [1,2,3], 
     (filter, lambda x: x % 2 == 1), 
     (map, lambda x: x+3) 
    ) 
    #=> [4, 6] 
    """ 
    for func_and_args in funcs_and_args: 
     func, *b = func_and_args 
     arg = func(*b, arg) 
    return(arg) 
0

Questo sembra funzionare per semplice input. Non è sicuro che valga lo sforzo per l'input complesso, ad esempio ((42, 'spam'), {'spam': 42}).

def compose(function, *functions): 
    return function if not functions else \ 
      lambda *args, **kwargs: function(compose(*functions)(*args, **kwargs)) 

def rcompose(*functions): 
    return compose(*reversed(functions)) 

def postfix(arg, *functions): 
    return rcompose(*functions)(arg) 

Esempio:

>>> postfix(1, str, len, hex) 
'0x1' 
>>> postfix(1, hex, len) 
3