Comment écrire une fonction qui renvoie une autre fonction?

93

En Python, j'aimerais écrire une fonction make_cylinder_volume(r)qui renvoie une autre fonction. Cette fonction retournée doit pouvoir être appelée avec un paramètre het renvoyer le volume d'un cylindre avec hauteur het rayon r.

Je sais comment renvoyer des valeurs à partir de fonctions en Python, mais comment renvoyer une autre fonction ?

Julian Das
la source

Réponses:

191

Essayez ceci, en utilisant Python:

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

Utilisez-le comme ceci, par exemple avec radius=10et height=5:

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

Notez que retourner une fonction consistait simplement à définir une nouvelle fonction à l'intérieur de la fonction et à la renvoyer à la fin - en prenant soin de passer les paramètres appropriés pour chaque fonction. Pour info, la technique de renvoi d'une fonction à partir d'une autre fonction est connue sous le nom de currying .

Óscar López
la source
1
Donc le que 10vous avez passé est stocké quelque part? Quand est-il ramassé?
sudo
4
@sudo jetez un œil à en.wikipedia.org/wiki/Closure_(computer_programming)
David Hernandez
19

À l'aide de lambdas, également appelées fonctions anonymes, vous pouvez extraire la volumefonction à l'intérieur du make_cylinder_volume_funcsur une seule ligne. En aucun cas différent de la réponse d'Óscar López, la solution utilisant lambda est toujours dans un sens «plus fonctionnelle».

Voici comment vous pouvez écrire la réponse acceptée à l'aide d'une expression lambda:

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

Et puis appelez comme vous le feriez pour n'importe quelle autre fonction curry:

volume_radius_1 = make_cylinder_volume_fun(1)
volume_radius_1(1) 
=> 3.141592653589793
DaveIdito
la source
Je sais que vous répondez à ce qui a été demandé, mais pour le bien de ma compréhension, si elle lambda h:était supprimée, la fonction fonctionnerait-elle de la même manière?
schoon
2
@schoon Non, cela ne fonctionnera pas dans ce cas. C'est en fait un cas très intéressant pour mettre en évidence l'idée de «portée variable» et de currying de fonction (qui repose essentiellement sur la portée varibale). La raison pour laquelle cela ne fonctionne pas (dans mon exemple) est que le returnva essayer d'évaluer le résultat avant de retourner, et parce que c'est un tas de variables, il retournera une valeur flottante (essayez de renvoyer une fonction et cela fonctionnera). lambdaindique que le code suivant ne doit pas être évalué et que la portée de la variable r sera conservée dans les fonctions renvoyées par make_cylinder...
DaveIdito
10

Je veux juste souligner que vous pouvez le faire avec pymonad

 import pymonad 

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

 add5 = add(5)
 add5(4)
 9
Érotémique
la source
1
from functools import partial add5 = partial(add, 5)Fait exactement la même chose
R3ctor
1

Je sais que je suis trop tard à la fête, mais je pense que vous pourriez trouver cette solution intéressante.

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

Voici la documentation sur le partialfonctionnement.

R3ctor
la source