Jak napisać funkcję, która zwraca inną funkcję?

93

W Pythonie chciałbym napisać funkcję, make_cylinder_volume(r)która zwraca inną funkcję. Ta zwrócona funkcja powinna być wywoływalna za pomocą parametru hi zwracać objętość walca wraz z wysokością hi promieniem r.

Wiem, jak zwrócić wartości z funkcji w Pythonie, ale jak zwrócić inną funkcję ?

Julian Das
źródło

Odpowiedzi:

191

Spróbuj tego, używając Pythona:

import math
def make_cylinder_volume_func(r):
    def volume(h):
        return math.pi * r * r * h
    return volume

Użyj go w ten sposób, na przykład z radius=10i height=5:

volume_radius_10 = make_cylinder_volume_func(10)
volume_radius_10(5)
=> 1570.7963267948967

Zwróć uwagę, że zwrócenie funkcji było prostą kwestią zdefiniowania nowej funkcji wewnątrz funkcji i zwrócenia jej na końcu - uważając, aby przekazać odpowiednie parametry dla każdej funkcji. FYI, technika zwracania funkcji z innej funkcji jest znana jako currying .

Óscar López
źródło
1
Więc 10przekazany przez Ciebie plik jest gdzieś przechowywany? Kiedy jest zbierany jako śmieci?
sudo
4
@sudo spójrz na en.wikipedia.org/wiki/Closure_(computer_programming)
David Hernandez
19

Używając wyrażeń lambd, zwanych również funkcjami anonimowymi, możesz wyodrębnić volumefunkcję wewnątrz elementu make_cylinder_volume_funcdo pojedynczego wiersza. W niczym nie różni się od odpowiedzi Óscara Lópeza, rozwiązanie wykorzystujące lambdę jest nadal w pewnym sensie „bardziej funkcjonalne”.

Oto jak możesz napisać zaakceptowaną odpowiedź za pomocą wyrażenia lambda:

import math
def make_cylinder_volume_fun(r):
    return lambda h: math.pi * r * r * h

A potem zadzwoń tak jak każdą inną funkcję curry:

volume_radius_1 = make_cylinder_volume_fun(1)
volume_radius_1(1) 
=> 3.141592653589793
DaveIdito
źródło
Zdaję sobie sprawę, że odpowiadasz na to, o co prosiłem, ale dla mojego zrozumienia, czy gdyby lambda h:zostało usunięte, czy funkcja działałaby tak samo?
szkun
2
@schoon Nie, w tym przypadku nie zadziała. W rzeczywistości jest to bardzo interesujący przypadek, aby podkreślić ideę „zakresu zmiennego” i curry funkcji (która zasadniczo polega na określaniu zakresu zmiennego). Powodem, dla którego to nie działa (w moim przykładzie) jest to, że returnspróbuje ocenić wynik przed zwróceniem, a ponieważ jest to zbiór zmiennych, zwróci jakąś wartość zmiennoprzecinkową (spróbuj zwrócić funkcję i zadziała). lambdamówil, że poniższy kod nie powinien być oceniany, a także, że zakres zmiennej r zostanie zachowany w funkcjach zwracanych przez make_cylinder...
DaveIdito
10

Chcę tylko zaznaczyć, że możesz to zrobić za pomocą pymonad

 import pymonad 

 @pymonad.curry
 def add(a, b):
     return a + b

 add5 = add(5)
 add5(4)
 9
Erotyczne
źródło
1
from functools import partial add5 = partial(add, 5)Robi dokładnie to samo
R3ctor
1

Wiem, że jestem za późno na imprezę, ale myślę, że to rozwiązanie może Ci się spodobać.

from math import pi
from functools import partial

def cylinder_volume(r, h):
    return pi * r * r * h

make_cylinder_with_radius_2 = partial(cylinder_volume, 2)
make_cylinder_with_height_3 = partial(cylinder_volume, h=3)

print(cylinder_volume(2, 3))            # 37.6991118431
print(make_cylinder_with_radius_2(3))   # 37.6991118431
print(make_cylinder_with_height_3(2))   # 37.6991118431

Oto dokumentacja o tym, jak partialdziała.

R3ctor
źródło