Como faço para escrever uma função que retorna outra função?

97

Em Python, gostaria de escrever uma função make_cylinder_volume(r)que retorne outra função. Essa função retornada deve ser chamada com um parâmetro he retornar o volume de um cilindro com altura he raio r.

Eu sei como retornar valores de funções em Python, mas como faço para retornar outra função ?

Julian Das
fonte

Respostas:

194

Experimente isso, usando Python:

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

Use-o assim, por exemplo, com radius=10e height=5:

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

Observe que retornar uma função era uma simples questão de definir uma nova função dentro da função e retorná-la no final - tendo o cuidado de passar os parâmetros apropriados para cada função. FYI, a técnica de retornar uma função de outra função é conhecida como currying .

Óscar López
fonte
1
Então, o que 10você passou está armazenado em algum lugar? Quando é coletado o lixo?
sudo
4
@sudo dê uma olhada em en.wikipedia.org/wiki/Closure_(computer_programming)
David Hernandez
20

Usando lambdas, também conhecidas como funções anônimas, você pode abstrair a volumefunção dentro de make_cylinder_volume_funcem uma única linha. Em nada diferente da resposta de Óscar López, a solução usando lambda ainda é em certo sentido 'mais funcional'.

É assim que você pode escrever a resposta aceita usando uma expressão lambda:

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

E então chame como você faria qualquer outra função curry:

volume_radius_1 = make_cylinder_volume_fun(1)
volume_radius_1(1) 
=> 3.141592653589793
DaveIdito
fonte
Percebi que você está respondendo o que foi solicitado, mas pelo meu entendimento, se lambda h:fosse retirada a função funcionaria da mesma forma?
escola
2
@schoon Não, não funcionará neste caso. Este é realmente um caso muito interessante para destacar a ideia de 'escopo variável' e currying de função (que basicamente depende do escopo varibale). A razão pela qual não funciona (no meu exemplo) é porque o returntentará avaliar o resultado antes de retornar e, como é um monte de variáveis, retornará algum valor flutuante (tente retornar uma função e ela funcionará). lambdainforma que o código a seguir não deve ser avaliado e também que o escopo da variável r será preservado nas funções retornadas por make_cylinder...
DaveIdito
10

Só quero salientar que você pode fazer isso com pymonad

 import pymonad 

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

 add5 = add(5)
 add5(4)
 9
Erotêmico
fonte
1
from functools import partial add5 = partial(add, 5)Faz exatamente o mesmo
R3ctor
1

Sei que estou muito atrasado para a festa, mas acho que você pode achar esta solução interessante.

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

Aqui está a documentação sobre como partialfunciona.

R3ctor
fonte
-1

Aqui está um grande exemplo que cobre muitos dos casos de argumentos múltiplos em uma função

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'")

Aqui, primeiro chame a função matemática com a operação necessária e, em seguida, use a função retornada para o cálculo real

Rahul Vaidya
fonte