J'ai une liste d'objets et je souhaite supprimer tous les objets vides sauf un, en utilisant filter
et une lambda
expression.
Par exemple, si l'entrée est:
[Object(name=""), Object(name="fake_name"), Object(name="")]
... alors la sortie devrait être:
[Object(name=""), Object(name="fake_name")]
Existe-t-il un moyen d'ajouter une affectation à une lambda
expression? Par exemple:
flag = True
input = [Object(name=""), Object(name="fake_name"), Object(name="")]
output = filter(
(lambda o: [flag or bool(o.name), flag = flag and bool(o.name)][0]),
input
)
Réponses:
L'opérateur d'expression d'affectation
:=
ajouté dans Python 3.8 prend en charge l'affectation à l'intérieur des expressions lambda. Cet opérateur ne peut apparaître que dans une expression(...)
entre parenthèses , entre crochets[...]
ou accolades{...}
pour des raisons syntaxiques. Par exemple, nous pourrons écrire ce qui suit:Dans Python 2, il était possible d'effectuer des affectations locales comme effet secondaire de la compréhension de liste.
Cependant, il n'est pas possible d'utiliser l'un ou l'autre de ces éléments dans votre exemple car votre variable se
flag
trouve dans une portée externe et non dans lalambda
portée de. Cela n'a pas à voir aveclambda
, c'est le comportement général de Python 2. Python 3 vous permet de contourner cela avec lenonlocal
mot - clé à l'intérieur dedef
s, maisnonlocal
ne peut pas être utilisé danslambda
s.Il y a une solution de contournement (voir ci-dessous), mais pendant que nous sommes sur le sujet ...
Dans certains cas, vous pouvez utiliser ceci pour tout faire à l'intérieur d'un
lambda
:Veuillez ne pas le faire.
... retour à votre exemple d'origine: bien que vous ne puissiez pas effectuer de tâches
flag
variable dans la portée externe, vous pouvez utiliser des fonctions pour modifier la valeur précédemment attribuée.Par exemple,
flag
pourrait être un objet dont.value
nous définissons en utilisantsetattr
:Si nous voulions adapter le thème ci-dessus, nous pourrions utiliser une compréhension de liste au lieu de
setattr
:Mais vraiment, dans un code sérieux, vous devriez toujours utiliser une définition de fonction régulière au lieu d'une
lambda
si vous allez faire une affectation externe.la source
.setattr()
et semblables (les dictionnaires devraient faire aussi bien, par exemple) pour pirater les effets secondaires dans le code fonctionnel de toute façon, du code sympa de @JeremyBanks a été montré :)assignment operator
!Vous ne pouvez pas vraiment maintenir l'état dans une expression
filter
/lambda
(à moins d'abuser de l'espace de noms global). Vous pouvez cependant obtenir quelque chose de similaire en utilisant le résultat accumulé transmis dans unereduce()
expression:Vous pouvez, bien sûr, modifier un peu la condition. Dans ce cas, il filtre les doublons, mais vous pouvez également utiliser
a.count("")
, par exemple, pour limiter uniquement les chaînes vides.Inutile de dire que vous pouvez le faire, mais vous ne devriez vraiment pas. :)
Enfin, vous pouvez tout faire en Python pur
lambda
: http://vanderwijk.info/blog/pure-lambda-calculus-python/la source
Il n'est pas nécessaire d'utiliser un lambda, lorsque vous pouvez supprimer tous les null, et en remettre un si la taille d'entrée change:
la source
output = [x for x in input if x.name]
.L'affectation normale (
=
) n'est pas possible dans unelambda
expression, bien qu'il soit possible d'effectuer diverses astuces avecsetattr
et entre amis.Cependant, résoudre votre problème est en fait assez simple:
qui te donnera
Comme vous pouvez le voir, il conserve la première instance vide au lieu de la dernière. Si vous avez besoin du dernier à la place, inversez la liste entrant dans
filter
et inversez la liste qui sort defilter
:qui te donnera
Une chose à savoir: pour que cela fonctionne avec des objets arbitraires, ces objets doivent être correctement implémentés
__eq__
et__hash__
comme expliqué ici .la source
MISE À JOUR :
ou en utilisant
filter
etlambda
:Réponse précédente
OK, êtes-vous bloqué sur l'utilisation du filtre et du lambda?
Il semble que ce serait mieux servi avec une compréhension du dictionnaire,
Je pense que la raison pour laquelle Python n'autorise pas l'affectation dans un lambda est similaire à la raison pour laquelle il n'autorise pas l'affectation dans une compréhension et cela a quelque chose à voir avec le fait que ces choses sont évaluées sur le
C
côté et peuvent donc nous donner un augmentation de la vitesse. Du moins, c'est mon impression après avoir lu l' un des essais de Guido .Je suppose que cela irait également à l'encontre de la philosophie d'avoir une seule bonne façon de faire une seule chose en Python.
la source
TL; DR: Lorsque vous utilisez des idiomes fonctionnels, il est préférable d'écrire du code fonctionnel
Comme beaucoup de gens l'ont souligné, en Python, l'attribution de lambdas n'est pas autorisée. En général, lorsque vous utilisez des idiomes fonctionnels, il vaut mieux penser de manière fonctionnelle, ce qui signifie dans la mesure du possible pas d'effets secondaires et pas de tâches.
Voici une solution fonctionnelle qui utilise un lambda. J'ai assigné le lambda à
fn
pour plus de clarté (et parce qu'il est un peu long).Vous pouvez également faire cette affaire avec des itérateurs plutôt que des listes en changeant un peu les choses. Vous avez également des importations différentes.
Vous pouvez toujours réorganiser le code pour réduire la longueur des instructions.
la source
Si au lieu de cela,
flag = True
nous pouvons faire une importation à la place, alors je pense que cela répond aux critères:Ou peut-être que le filtre est mieux écrit comme suit:
Ou, juste pour un simple booléen, sans aucune importation:
la source
La manière pythonique de suivre l'état pendant l'itération est d'utiliser des générateurs. La manière itertools est assez difficile à comprendre à mon humble avis et essayer de pirater des lambdas pour le faire est tout simplement ridicule. J'essaierais:
Dans l'ensemble, la lisibilité l'emporte à chaque fois sur la compacité.
la source
Non, vous ne pouvez pas placer une affectation dans un lambda en raison de sa propre définition. Si vous utilisez la programmation fonctionnelle, vous devez supposer que vos valeurs ne sont pas modifiables.
Une solution serait le code suivant:
la source
Si vous avez besoin d'un lambda pour mémoriser l'état entre les appels, je recommanderais soit une fonction déclarée dans l'espace de noms local, soit une classe avec une surcharge
__call__
. Maintenant que toutes mes mises en garde contre ce que vous essayez de faire sont écartées, nous pouvons obtenir une réponse concrète à votre question.Si vous avez vraiment besoin d'avoir votre lambda pour avoir de la mémoire entre les appels, vous pouvez le définir comme:
Ensuite, il vous suffit de passer
f
àfilter()
. Si vous en avez vraiment besoin, vous pouvez récupérer la valeur deflag
avec les éléments suivants:Vous pouvez également modifier l'espace de noms global en modifiant le résultat de
globals()
. Malheureusement, vous ne pouvez pas modifier l'espace de noms local de la même manière que la modification du résultat delocals()
n'affecte pas l'espace de noms local.la source
(let ((var 42)) (lambda () (setf var 43)))
.Vous pouvez utiliser une fonction de liaison pour utiliser une pseudo-instruction lambda à plusieurs instructions. Ensuite, vous pouvez utiliser une classe wrapper pour un indicateur afin d'activer l'affectation.
la source
Une sorte de solution de contournement compliquée, mais l'affectation dans les lambdas est de toute façon illégale, donc cela n'a pas vraiment d'importance. Vous pouvez utiliser la
exec()
fonction intégrée pour exécuter l'affectation à partir de l'intérieur du lambda, comme cet exemple:la source
d'abord, vous n'avez pas besoin d'utiliser une affectation locale pour votre travail, vérifiez simplement la réponse ci-dessus
deuxièmement, il est simple d'utiliser locals () et globals () pour obtenir la table des variables, puis changer la valeur
vérifiez cet exemple de code:
si vous devez changer l'ajout d'une variable globale à votre environnement, essayez de remplacer locals () par globals ()
La liste de compilation de python est cool mais la plupart des projets triditionnels n'acceptent pas cela (comme flask: [)
j'espère que ça pourrait aider
la source
locals()
, il dit explicitement dans la documentation que le changer ne change pas réellement la portée locale (ou du moins ce ne sera pas toujours).globals()
d'autre part fonctionne comme prévu.globals()
. pastebin.com/5Bjz1mR4 (testé en 2.6 et 3.2) le prouve.