107

In Python, I'd like to write a function make_cylinder_volume(r) which returns another function. That returned function should be callable with a parameter h, and return the volume of a cylinder with height h and radius r.

I know how to return values from functions in Python, but how do I return another function?

Julian Das
  • 1,351
  • 3
  • 11
  • 9

5 Answers5

206

Try this, using Python:

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

Use it like this, for example with radius=10 and height=5:

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

Notice that returning a function was a simple matter of defining a new function inside the function, and returning it at the end - being careful to pass the appropriate parameters for each function. FYI, the technique of returning a function from another function is known as currying.

Óscar López
  • 232,561
  • 37
  • 312
  • 386
  • 1
    So the `10` you passed in is stored somewhere? When does it get garbage-collected? – sudo May 18 '16 at 23:02
  • 4
    @sudo take a look at https://en.wikipedia.org/wiki/Closure_(computer_programming) – David Hernandez Oct 06 '16 at 23:41
  • If you came here and also want to do type-hinting, go here: https://stackoverflow.com/questions/42705709/how-to-type-hint-a-function-that-returns-a-function – Ant Jul 20 '23 at 07:13
24

Using lambdas, also known as anonymous functions, you can abstract out the volume function inside the make_cylinder_volume_func to a single line. In no way different from Óscar López's answer, the solution using lambda is still in a sense 'more functional'.

This is how you can write the accepted answer using a lambda expression:

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

And then call as you'd any other curried function:

volume_radius_1 = make_cylinder_volume_fun(1)
volume_radius_1(1) 
=> 3.141592653589793
DaveIdito
  • 1,546
  • 14
  • 31
  • I realise you are answering what was requested, but for the sake of my understanding, if `lambda h:` was removed would the function work the same? – schoon Apr 05 '18 at 12:24
  • 2
    @schoon No it won't work in this case. This is actually a very interesting case to highlight the idea of 'variable scoping' and function currying (which basically relies on varibale scoping). The reason why it doesn't work (in my example) is because the `return` will try to evaluate the result before returning, and because it's a bunch of variables, it will return some float value (try returning a function and it will work). `lambda` tellsl that the following code shouldn't be evaluated and also that the scope of variable r will be preserved in functions returned by `make_cylinder..`. – DaveIdito Apr 11 '18 at 00:44
10

Just want to point out that you can do this with pymonad

 import pymonad 

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

 add5 = add(5)
 add5(4)
 9
Erotemic
  • 4,806
  • 4
  • 39
  • 80
2

I know I am too late to the party, but I think you might find this solution interesting.

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

Here is documentation about how partial works.

rmiguelrivero
  • 926
  • 8
  • 8
0

Here is one massive example which covers many of single multiple argument cases in one function

def maths(var='NA'):
if var.lower() == 'add':
    def add(*args):
        return "Sum is : "+str(sum(args))
    return add
elif var.lower() == 'substract':
    def substract(a,b):
        if a>b:
            return "Difference is : "+str(a-b)
        else:
            return "Difference is : "+str(b-a)
    return substract
elif var.lower() == 'multiply':
    def multiply(*args):
        temp = 1
        for x in args:
            temp = temp*x
        return "multiplication is : "+str(temp)
    return multiply
elif var.lower() == 'divide':
    def divide(a,b):
        return "Division is : "+str(a/b)
    return divide
else:
    print("Please choose one of given operations: 'add','substract','multiply','divide'")

Here first call the maths function with the required operation and then use the returned function for the actual calculation