web-dev-qa-db-fra.com

Pourquoi les assignations ne sont-elles pas autorisées dans les expressions `lambda` de Python?

Ceci n’est pas une copie de Affectation dans une expression lambda en Python , c’est-à-dire que je suis non demande comment tromper Python en lui attribuant une expression lambda.

J'ai un fond de λ-calcul. Compte tenu du code suivant, il semble que Soit comme si Python était tout à fait disposé à effectuer des effets secondaires dans lambda Expressions:

#!/usr/bin/python

def applyTo42(f):
    return f(42)

def double(x):
    return x * 2

class ContainsVal:
    def __init__(self, v):
        self.v = v

    def store(self, v):
        self.v = v

def main():

    print('== functional, no side effects')

    print('-- print the double of 42')
    print(applyTo42(double))

    print('-- print 1000 more than 42')
    print(applyTo42(lambda x: x + 1000))

    print('-- print c\'s value instead of 42')
    c = ContainsVal(23)
    print(applyTo42(lambda x: c.v))


    print('== not functional, side effects')

    print('-- perform IO on 42')
    applyTo42(lambda x: print(x))

    print('-- set c\'s value to 42')
    print(c.v)
    applyTo42(lambda x: c.store(x))
    print(c.v)

    #print('== illegal, but why?')
    #print(applyTo42(lambda x: c.v = 99))

if __== '__main__':
    main()

Mais si je dégage les lignes

    print('== illegal, but why?')
    print(applyTo42(lambda x: c.v = 99))

J'aurais

SyntaxError: lambda cannot contain assignment

Pourquoi pas? Quelle est la raison la plus profonde derrière tout ça?

  • Comme le code le démontre, il ne peut s'agir de «pureté» au sens fonctionnel.

  • La seule explication que je puisse imaginer est que les assignemts ne renvoient rien, pas même None. Mais cela semble boiteux et serait facile à résoudre (une solution: faire en sorte que les expressions lambda retournent None si Body est une déclaration).

Pas une réponse:

  • Parce que c'est défini comme ça (je veux savoir pourquoi c'est défini comme ça).

  • Parce que c'est dans la grammaire (voir ci-dessus).

  • Utilisez def si vous avez besoin d'instructions (je n'ai pas demandé comment obtenir des déclarations Dans une fonction).

“Cela changerait la syntaxe/le langage/la sémantique” serait correct si vous pouviez trouver un exemple d'un tel changement et pourquoi il serait mauvais.

12
stefan

La raison entière lambda existe, c’est qu’il s’agit d’une expression.1 Si vous voulez quelque chose qui ressemble à lambda mais est une déclaration, c'est juste def.

Les expressions Python ne peuvent pas contenir d'instructions. C’est en fait fondamental pour le langage et Python tire un grand profit de cette décision. C'est la raison pour laquelle l'indentation pour le contrôle de flux fonctionne au lieu d'être maladroite comme dans beaucoup d'autres tentatives (comme CoffeeScript). C'est la raison pour laquelle vous pouvez lire les changements d'état en survolant le premier objet de chaque ligne. Cela fait même partie des raisons pour lesquelles le langage est facile à analyser, à la fois pour le compilateur et pour les lecteurs humains.2

Changer Python pour avoir un moyen "d'échapper" à la division déclaration-expression, sauf peut-être d'une manière très prudente et limitée, le transformerait en un langage complètement différent, qui ne présente plus beaucoup des avantages qui incitent les gens à choisir Python en premier lieu.

Changer Python pour faire la plupart des expressions, comme Ruby, le transformerait à nouveau en un langage complètement différent, sans les avantages actuels de Python.

Et si Python avait apporté l'un ou l'autre de ces changements, il n'y aurait plus de raison pour lambda en premier lieu;2,3 vous pouvez simplement utiliser des instructions def à l'intérieur d'une expression.


Pourquoi ne pas changer Python pour créer des expressions d'assignations? Eh bien, il devrait être évident que casser "vous pouvez lire les changements d'état en écrémant le premier objet de chaque ligne". Bien que Guido se concentre généralement sur le fait que if spam=eggs est une erreur plus souvent qu’une chose utile.

Le fait que Python vous permette de contourner cela au besoin, comme setattr ou même d'appeler explicitement __setitem__ sur globals(), ne signifie pas que c'est quelque chose qui devrait avoir un support syntaxique direct. Quelque chose dont on a très rarement besoin ne mérite pas de sucre syntaxique - et encore plus pour quelque chose d'assez inhabituel qui devrait soulever des sourcils et/ou des drapeaux rouges quand il le fait.


1. Je ne sais pas si c'est ce que comprenait Guido lorsqu'il a ajouté lambda dans Python 1.0. Mais c’est certainement la raison pour laquelle lambda n’a pas été supprimé de Python 3.0.

2. En fait, Guido a suggéré à plusieurs reprises qu'autoriser un analyseur syntaxique LL (1) que les humains pouvaient exécuter dans la tête était une raison suffisante pour que le langage soit basé sur des instructions, au point que d'autres avantages ne sont même plus nécessaires. être discuté. J'ai écrit à ce sujet il y a quelques années si quelqu'un est intéressé.

3. Si vous vous demandez pourquoi tant de langues do ont une expression lambda alors qu'elles ont déjà def: Dans de nombreuses langues, allant de C++ à Ruby, les fonctions ne sont pas des objets de première classe qui peuvent être échangés, donc ils ont dû inventer une deuxième chose qui est de première classe mais qui fonctionne comme une fonction. Dans d’autres, de Smalltalk à Java, les fonctions n’existent même pas exist, il ne s’agit que de méthodes, alors encore une fois, elles ont dû inventer une deuxième chose qui n’est pas une méthode mais qui fonctionne comme telle. Python n'a aucun de ces problèmes.

4. Quelques langages, tels que C # et JavaScript, avaient en fait des définitions de fonctions intégrées parfaitement fonctionnelles, mais ont ajouté une sorte de syntaxe lambda en tant que sucre syntaxique pur, pour le rendre plus concis et moins chaud. Cela pourrait en fait valoir la peine d’être fait en Python (bien que chaque tentative d’une bonne syntaxe ait échoué jusqu’à présent), mais ce ne serait pas la syntaxe actuelle lambda, qui est presque aussi détaillée que def.

15
abarnert

Il y a un problème syntax: une affectation est un statement, et le corps d'un lambda ne peut avoir que expressions. La syntaxe de Python est conçue de cette façon1. Découvrez-le sur https://docs.python.org/3/reference/grammar.html .

Il y a aussi un problème sémantique: qu'est-ce que chaque instruction renvoie?

Je ne pense pas qu'il soit intéressant de changer cela, car les lambdas sont destinés à des codes très simples et courts. De plus, une déclaration autoriserait également séquences d'instructions, ce qui n'est pas souhaitable pour lambdas.

Il pourrait également être corrigé en autorisant de manière sélective certaines instructions dans le corps lambda et en spécifiant la sémantique (par exemple, une affectation renvoie None, ou renvoie la valeur attribuée; cette dernière me semble plus logique). Mais quel est l'avantage?

Lambdas et les fonctions sont interchangeables. Si vous avez réellement un cas d'utilisation pour une instruction particulière dans le corps d'un lambda, vous pouvez définir une fonction qui l'exécute et votre problème spécifique est résolu.


Peut-être que vous pouvez créer une macro syntaxique pour autoriser cela avec MacroPy3 (je suis en train de deviner, étant donné que je suis un fan du projet, mais que je n’ai toujours pas eu le temps de plonger dedans).

Par exemple, MacroPy vous permettrait de définir une macro qui transforme f[_ * _] en lambda a, b: a * b. Il ne devrait donc pas être impossible de définir la syntaxe d'un lambda qui appelle une fonction que vous avez définie.


1 Une bonne raison de ne pas le changer est que cela gênerait la syntaxe, car un lambda peut être à des endroits où des expressions peuvent être. Et les déclarations ne devraient pas. Mais c'est une remarque très subjective de ma part.

6
fferri

Ma réponse est basée sur le commentaire de chepner ci-dessus et ne s'appuie sur aucune autre source crédible ou officielle, mais je pense que cela sera utile.

Si l'affectation était autorisée dans les expressions lambda, alors l'erreur consistant à confondre == (test d'égalité) avec = (affectation) aurait plus de chances de s'échapper.

Exemple:

>>> # Correct use of equality test
... list(filter(lambda x: x==1, [0, 1, 0.0, 1.0, 0+0j, 1+0j]))
[1, 1.0, (1+0j)]

>>> # Suppose that assignment is used by mistake instead of equality testing
... # and the return value of an assignment expression is always None
... list(filter(lambda x: None, [0, 1, 0.0, 1.0, 0+0j, 1+0j]))
[]

>>> # Suppose that assignment is used by mistake instead of equality testing
... # and the return value of an assignment expression is the assigned value
... list(filter(lambda x: 1, [0, 1, 0.0, 1.0, 0+0j, 1+0j]))
[0, 1, 0.0, 1.0, 0j, (1+0j)]
2
Leon

Tant que exec() (et eval()) est autorisé dans lambda, vous pouvez effectuer des affectations dans lambda:

q = 3

def assign(var_str, val_str):
    exec("global " + var_str + "; " + 
    var_str + " = " + val_str)

lambda_assign = lambda var_str, val_str: assign(var_str, val_str)

q ## gives: 3

lambda_assign("q", "100")

q ## gives: 100

## what would such expression be a win over the direct:

q = 100

## ? `lambda_assign("q", "100")` will be for sure slower than
##   `q = 100` isn't it?

q_assign = lambda v: assign("q", v)

q_assign("33")

q ## 33

## but do I need lambda for q_assign?

def q_assign(v): assign("q", v) 

## would do it, too, isn't it?

Mais puisque les expressions lambda ne permettent de définir qu'une seule expression dans leur corps (au moins en Python ...), à quoi servirait de permettre une affectation dans un lambda? Son effet net serait d’affecter directement (sans utiliser de lambda) q = 100, n’est-ce pas?

Ce serait encore plus rapide que de le faire sur un lambda défini, puisque vous avez au moins une fonction à rechercher et à exécuter moins à exécuter ...

1
Gwang-Jin Kim

Il n'y a pas vraiment de raisons plus profondes, cela n'a rien à voir avec les conceptions de langages fonctionnels ou lambda, c'est juste pour éviter aux programmeurs de mélanger des opérateurs = et ==, ce qui est une erreur très courante dans d'autres langages.

SI cette histoire a plus à faire, je suppose que c’est peut-être une bonne idée, car python bdfl GVR a exprimé ses côtés non aimants envers lambda et d’autres fonctionnalités et a tenté (et a concédé) de les supprimer de python 3 https://www.artima.com /weblogs/viewpost.jsp?thread=98196

Au moment de la rédaction de cet article, les développeurs principaux ont récemment eu une discussion animée sur l'opportunité d'inclure une assignation d'expression contraignante avec un nom limité, le débat est toujours en cours, alors peut-être qu'un jour nous le verrons dans lambda (improbable)

Comme vous l'avez dit vous-même, il ne s'agit en aucun cas d'effets secondaires ou de pureté, ils ne veulent tout simplement pas que lambda soit plus qu'une expression unique ... ... ...

Cela dit, voici quelques informations sur les affectations multi-expressions dans lambda, continuez à lire si vous êtes intéressé.

Ce n'est pas du tout impossible en python, en fait il était parfois nécessaire de capturer les liaisons variables et les contournements tardifs avec (ab) en utilisant kwargs (arguments de mots clés)

modifier:

exemple de code

f = lambda x,a=1: (lambda c = a+2, b = a+1: (lambda e = x,d = c+1: print(a,b,c,d,e))())()

f("w")

# output 1 2 3 4 w

# expression assignment through an object's method call

if let(a=1) .a > 0 and let(b=let.a+1) .b != 1 and let(c=let.b+let.a) .c:
    print(let.a, let.b, let.c)

# output 1 2 3
1
guramarx

Je pense que tous les gars ont déjà répondu à cela. Nous utilisons principalement la fonction lambdas lorsque nous voulons simplement:

-créer quelques fonctions simples qui fonctionnent parfaitement dans un endroit spécifique (la plupart du temps cachées dans d'autres grandes fonctions - La fonction lambda n'a pas de nom - peut être utilisée avec d'autres fonctions intégrées des fonctions telles que map, list, etc.

>>> Celsius = [39.2, 36.5, 37.3, 37.8] 
>>> Fahrenheit = map(lambda x: (float(9)/5)*x + 32, Celsius) # mapping the list here  
>>> print Fahrenheit
[102.56, 97.700000000000003, 99.140000000000001, 100.03999999999999]

S'il vous plaît visitez cette page Web, cela pourrait être utile. Gardez-le! https://www.python-course.eu/lambda.php

0
Jonas Amara

Dans l'état actuel des choses, Python a été conçu comme un langage basé sur des instructions. Par conséquent, l'affectation et les autres associations de noms sont des instructions et n'ont aucun résultat.

Les développeurs Python sont en train de discuter de PEP 572, qui introduirait une expression name-binding .

0
holdenweb