10

Pracuję w języku Python. Niedawno odkryłem cudowny mały pakiet o nazwie fn. Używam go do komponowania funkcji.Lepsza kompozycja funkcji w języku Python

Na przykład zamiast:

baz(bar(foo(x)))) 

z FN, można napisać:

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

Kiedy zobaczyłem to, od razu pomyślałem o Clojure:

(-> x foo bar baz) . 

Ale zauważ, że w Clojure dane wejściowe znajdują się po lewej stronie. Zastanawiam się, czy to możliwe w python/fn.

+2

Podczas gdy operator przeciążenia zachowanie jest interesujące dla mnie to po prostu wydaje się złe rzeczy do zrobienia w prawdziwym kodzie. –

+0

Nie ma sposobu na wykonanie tej dokładnej składni w Pythonie, jeśli o to pytasz. Możesz go przybliżać na różne sposoby. Co dokładnie jest dla ciebie ważne pod względem składni? – BrenBarn

+0

Utrzymywanie przepływu od lewej do prawej.Obecnie argument funkcji złożonej jest na końcu. Byłoby jaśniej, gdybym mógł napisać F() >> x >> foo >> bar >> baz lub podobny. –

Odpowiedz

9

Nie można replikować dokładnej składni, ale można zrobić coś podobnego:

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

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

    return result 

wydaje się działać:

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

nie można uzyskać tego dokładnej składni, choć można uzyskać coś w rodzaju F(x)(foo, bar, baz). Oto prosty przykład:

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 

ten jest nieco inny od odpowiedzi Blendera ponieważ przechowuje argument, który może później zostać ponownie wykorzystane z różnymi funkcjami.

Jest to coś w rodzaju przeciwieństwa normalnej funkcji: zamiast określać funkcję z góry i pozostawić pewne argumenty, które zostaną określone później, należy podać argument i pozostawić funkcję (funkcje) do określenia później. To interesująca zabawka, ale trudno jest pomyśleć, dlaczego tak naprawdę chcesz.

1

Jeśli chcesz użyć fn, z odrobiną włamania można dostać nieco bliżej Clojure składnię:

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

Zobacz jak dodałem inną funkcję na początku łańcucha kompozycję, która będzie po prostu wrócić x po wywołaniu. Problem polega na tym, że nadal musisz wywoływać swoją funkcję złożoną, po prostu bez żadnych argumentów.

Myślę, że odpowiedź @ Blendera jest najlepsza, próbując naśladować funkcję wątku Clojure w Pythonie.

0

wymyśliłem tego

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

To wydaje się działać za pośrednictwem prostego wejścia. Nie jesteś pewien, czy warto włożyć trudny wkład, np. ((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) 

Przykład:

>>> postfix(1, str, len, hex) 
'0x1' 
>>> postfix(1, hex, len) 
3 
Powiązane problemy