2010-12-06 10 views
8

Très rarement, je vais rencontrer du code en python qui utilise une fonction anonyme qui retourne une fonction anonyme ...?lambda retourne lambda en python

Malheureusement je ne peux pas trouver un exemple à portée de main, mais il prend généralement la forme comme ceci:

g = lambda x,c: x**c lambda c: c+1 

Pourquoi quelqu'un ferait cela? Peut-être que vous pouvez donner un exemple qui a du sens (je ne suis pas sûr que celui que j'ai fait ait un sens).

Edit: Voici un exemple:

swap = lambda a,x,y:(lambda f=a.__setitem__:(f(x,(a[x],a[y])), 
     f(y,a[x][0]),f(x,a[x][1])))() 
+3

rechercher et afficher un véritable exemple; celui-là n'est pas syntaxiquement correct. –

+0

produit SyntaxError: syntaxe non valide. Aucun sens deuxième lambda – joaquin

+0

Pourquoi quelqu'un ferait-il cela? Réponse: Pour irriter les autres? – joaquin

Répondre

13

Vous pouvez utiliser une telle construction pour faire taitement:

curry = lambda f, a: lambda x: f(a, x) 

Vous pourriez l'utiliser comme:

>>> add = lambda x, y: x + y 
>>> add5 = curry(add, 5) 
>>> add5(3) 
8 
+0

ce n'est généralement pas mieux fait avec functools.partial? – joaquin

+0

@joaquin: Oui. Je ne fais que présenter un exemple. –

1

Il peut être utile pour les espaces réservés temporaires. Supposons que vous ayez une usine de décorateur:

@call_logger(log_arguments=True, log_return=False) 
def f(a, b): 
    pass 

Vous pouvez remplacer temporairement avec

call_logger = lambda *a, **kw: lambda f: f 

Il peut également être utile si elle renvoie indirectement un lambda:

import collections 
collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(int))) 

Il est également utile pour créer des usines appelables dans la console Python.

Et juste parce que quelque chose est possible ne signifie pas que vous devez l'utiliser.

1

Ceci peut être utilisé pour extraire du code répétitif commun (il y a bien sûr d'autres façons d'y parvenir en python).

Peut-être écrivez-vous un enregistreur, et vous devez ajouter le niveau à la chaîne de journal. Vous pourriez écrire quelque chose comme:

import sys 
prefixer = lambda prefix: lambda message: sys.stderr.write(prefix + ":" + message + "\n") 
log_error = prefixer("ERROR") 
log_warning = prefixer("WARNING") 
log_info = prefixer("INFO") 
log_debug = prefixer("DEBUG") 

log_info("An informative message") 
log_error("Oh no, a fatal problem") 

Ce programme imprime sur

INFO:An informative message 
    ERROR:Oh no, a fatal problem 
1

Je l'ai fait quelque chose comme ça l'autre jour pour désactiver une méthode d'essai dans une suite unittest.

disable = lambda fn : lambda *args, **kwargs: None 

@disable 
test_method(self): 
    ... test code that I wanted to disable ... 

Facile à réactiver plus tard.

2
swap = lambda a,x,y:(lambda f=a.__setitem__:(f(x,(a[x],a[y])), 
     f(y,a[x][0]),f(x,a[x][1])))() 

Voir les parenthèses() à la fin? Le lambda interne n'est pas retourné, c'est appelé.

La fonction ne soit l'équivalent de

def swap(a, x, y): 
    a[x] = (a[x], a[y]) 
    a[y] = a[x][0] 
    a[x] = a[x][1] 

Mais supposons que nous voulons faire cela dans un lambda. Nous ne pouvons pas utiliser les affectations dans un lambda. Cependant, nous pouvons appeler __setitem__ pour le même effet.

def swap(a, x, y): 
    a.__setitem__(x, (a[x], a[y])) 
    a.__setitem__(y, a[x][0]) 
    a.__setitem__(x, a[x][1]) 

Mais pour un lambda, nous ne pouvons avoir qu'une seule expression. Mais puisque ce sont les appels de fonction, nous pouvons les envelopper dans un tuple

def swap(a, x, y): 
    (a.__setitem__(x, (a[x], a[y])), 
    a.__setitem__(y, a[x][0]), 
    a.__setitem__(x, a[x][1])) 

Cependant, tous ces __setitem__ « s sont me faire vers le bas, alors laissez-les factoriser:

def swap(a, x, y): 
    f = a.__setitem__ 
    (f(x, (a[x], a[y])), 
    f(y, a[x][0]), 
    f(x, a[x][1])) 

Dagnamit, je peux pas s'en tirer avec l'ajout d'une autre mission! Je sais que nous allons abuser des paramètres par défaut.

def swap(a, x, y): 
    def inner(f = a.__setitem__): 
     (f(x, (a[x], a[y])), 
     f(y, a[x][0]), 
     f(x, a[x][1])) 
    inner() 

Ok, nous allons passer à lambdas:

swap = lambda a, x, y: lambda f = a.__setitem__: (f(x, (a[x], a[y])), f(y, a[x][0]), f(x, a[x][1]))() 

Ce qui nous ramène à l'expression originale (plus/moins les fautes de frappe)

Tout cela ramène à la question: Pourquoi?

La fonction aurait dû être mis en œuvre comme

def swap(a, x, y): 
    a[x],a[y] = a[y],a[x] 

L'auteur original est allé sortir de sa façon d'utiliser un lambda plutôt alors une fonction. Il pourrait être qu'il n'aime pas la fonction imbriquée pour une raison quelconque. Je ne sais pas. Tout ce que je vais dire est son mauvais code. (sauf si il y a une justification mystérieuse pour cela.)

0

Il est le plus souvent - au moins dans le code que je rencontre et que j'écris moi-même - utilisé pour "geler" une variable avec la valeur qu'elle a au point lambda la fonction est créée. Dans le cas contraire, la variable nonlocals référence une variable dans la portée où elle existe, ce qui peut parfois conduire à des résultats non détectés.

Par exemple, si je veux créer une liste de dix fonctions, chacun étant un multiplicateur pour un scalaire de 0 à 9. On pourrait être tenté d'écrire comme ceci:

>>> a = [(lambda j: i * j) for i in range(10)] 
>>> a[9](10) 
90 

Celui qui, si vous voulez utiliser l'une des autres fonctions factoried vous obtenez le même résultat:

>>> a[1](10) 
90 

C'est parce que la variable « i » à l'intérieur du lambda n'est pas résolu lorsque le lambda est créé. Plutôt, Python garde une référence au "i" dans l'instruction "for" - sur la portée où il a été créé (cette référence est conservée dans la fermeture de la fonction lambda). Lorsque le lambda est exécuté, la variable est évaluée et sa valeur est la valeur finale dans cette portée.

Quand on utilise deux lambdas imbriquées comme celui-ci:

>>> a = [(lambda k: (lambda j: k * j))(i) for i in range(10)] 

La variable "i" est évaluée DURINT l'exécution de la boucle "pour". La valeur est transmise à "k" - et "k" est utilisé comme variable non locale dans la fonction de multiplicateur que nous sommes en train de factoriser. Pour chaque valeur de i, il y aura une instance différente de la fonction lambda englobante, et une valeur différente pour la variable "k".

Ainsi, il est possible d'atteindre l'objectif initial:

>>> a = [(lambda k: (lambda j: k * j))(i) for i in range(10)] 
>>> a[1](10) 
10 
>>> a[9](10) 
90 
>>> 
+0

Pourquoi ne pas utiliser: a = [(lambda k, i = i: i * j) pour j dans la plage (10)] à la place? –