Eu tenho uma lista de objetos e quero remover todos os objetos que estão vazios, exceto um, usando filter
e umlambda
expressão.
Por exemplo, se a entrada for:
[Object(name=""), Object(name="fake_name"), Object(name="")]
... então a saída deve ser:
[Object(name=""), Object(name="fake_name")]
Existe uma maneira de adicionar uma atribuição a uma lambda
expressão? Por exemplo:
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
)
Respostas:
O operador de expressão de atribuição
:=
adicionado no Python 3.8 suporta atribuição dentro de expressões lambda. Este operador só pode aparecer entre parênteses(...)
, colchetes[...]
ou colchetes{...}
expressão por motivos sintáticos. Por exemplo, seremos capazes de escrever o seguinte:No Python 2, era possível realizar atribuições locais como um efeito colateral das compreensões de lista.
No entanto, não é possível usar nenhum desses em seu exemplo porque sua variável
flag
está em um escopo externo, não olambda
escopo de. Isso não tem a ver comlambda
, é o comportamento geral em Python 2. Python 3 permite que você contorne isso com anonlocal
palavra - chave dentro dedef
s, masnonlocal
não pode ser usada dentro delambda
s.Há uma solução alternativa (veja abaixo), mas já que estamos no assunto ...
Em alguns casos, você pode usar isso para fazer tudo dentro de
lambda
:Por favor, não.
... de volta ao seu exemplo original: embora você não possa realizar atribuições à
flag
variável no escopo externo, você pode usar funções para modificar o valor atribuído anteriormente.Por exemplo,
flag
poderia ser um objeto cujo.value
definimos usandosetattr
:Se quiséssemos nos ajustar ao tema acima, poderíamos usar uma compreensão de lista em vez de
setattr
:Mas, realmente, em código sério, você sempre deve usar uma definição de função regular em vez de uma
lambda
se for fazer atribuições externas.fonte
.setattr()
and alikes ( dicionários devem servir também, por exemplo) para hackear efeitos colaterais em código funcional de qualquer maneira, código legal de @JeremyBanks foi mostrado :)assignment operator
!Você não pode realmente manter o estado em uma expressão
filter
/lambda
(a menos que esteja abusando do namespace global). No entanto, você pode conseguir algo semelhante usando o resultado acumulado transmitido em umareduce()
expressão:Você pode, é claro, ajustar um pouco a condição. Nesse caso, ele filtra duplicatas, mas você também pode usar
a.count("")
, por exemplo, para restringir apenas strings vazias.Desnecessário dizer que você pode fazer isso, mas realmente não deveria. :)
Por último, você pode fazer qualquer coisa em Python puro
lambda
: http://vanderwijk.info/blog/pure-lambda-calculus-python/fonte
Não há necessidade de usar um lambda, quando você pode remover todos os nulos e colocar um de volta se o tamanho de entrada mudar:
fonte
output = [x for x in input if x.name]
.A atribuição normal (
=
) não é possível dentro de umalambda
expressão, embora seja possível realizar vários truques comsetattr
e amigos.Resolver seu problema, no entanto, é na verdade muito simples:
o que vai te dar
Como você pode ver, ele mantém a primeira instância em branco em vez da última. Se você precisar do último em vez disso, inverta a lista indo para
filter
e inverta a lista saindo defilter
:o que vai te dar
Uma coisa a estar ciente: para que isso funcione com objetos arbitrários, esses objetos devem ser implementados corretamente
__eq__
e__hash__
conforme explicado aqui .fonte
ATUALIZAÇÃO :
ou usando
filter
elambda
:Resposta Anterior
OK, você está preso em usar filtro e lambda?
Parece que isso seria melhor servido com uma compreensão de dicionário,
Acho que o motivo pelo qual Python não permite atribuição em um lambda é semelhante ao motivo pelo qual ele não permite atribuição em uma compreensão e isso tem a ver com o fato de que essas coisas são avaliadas
C
lateralmente e, portanto, podem nos dar uma aumento na velocidade. Pelo menos essa é minha impressão depois de ler um dos ensaios de Guido .Meu palpite é que isso também iria contra a filosofia de ter uma maneira certa de fazer qualquer coisa em Python.
fonte
TL; DR: ao usar expressões funcionais, é melhor escrever código funcional
Como muitas pessoas apontaram, em Python a atribuição de lambdas não é permitida. Em geral, ao usar expressões funcionais, é melhor pensar de maneira funcional, o que significa, sempre que possível, nenhum efeito colateral e nenhuma atribuição.
Aqui está uma solução funcional que usa um lambda. Atribuí lambda para
fn
para maior clareza (e porque ficou um pouco longo).Você também pode fazer isso com iteradores em vez de listas, mudando um pouco as coisas. Você também tem algumas importações diferentes.
Você sempre pode reorganizar o código para reduzir o comprimento das instruções.
fonte
Se, em vez de
flag = True
, pudermos fazer uma importação, acho que isso atende aos critérios:Ou talvez o filtro seja melhor escrito como:
Ou, apenas para um booleano simples, sem nenhuma importação:
fonte
A maneira pythônica de rastrear o estado durante a iteração é com geradores. A forma de itertools é bastante difícil de entender IMHO e tentar hackear lambdas para fazer isso é simplesmente bobo. Eu tentaria:
No geral, a legibilidade sempre supera a compactação.
fonte
Não, você não pode colocar uma atribuição dentro de um lambda por causa de sua própria definição. Se você trabalha com programação funcional, deve assumir que seus valores não são mutáveis.
Uma solução seria o seguinte código:
fonte
Se você precisar de um lambda para lembrar o estado entre as chamadas, eu recomendaria uma função declarada no namespace local ou uma classe com um
__call__
. Agora que todas as minhas precauções contra o que você está tentando fazer estão fora do caminho, podemos obter uma resposta real à sua consulta.Se você realmente precisa ter seu lambda para ter alguma memória entre as chamadas, pode defini-lo como:
Então você só precisa passar
f
parafilter()
. Se realmente precisar, você pode recuperar o valor deflag
com o seguinte:Como alternativa, você pode modificar o namespace global modificando o resultado de
globals()
. Infelizmente, você não pode modificar o namespace local da mesma maneira que modificar o resultado delocals()
não afeta o namespace local.fonte
(let ((var 42)) (lambda () (setf var 43)))
.Você pode usar uma função de ligação para usar um lambda pseudo-multi-instrução. Em seguida, você pode usar uma classe de wrapper para um Sinalizador para habilitar a atribuição.
fonte
Uma espécie de solução alternativa complicada, mas a atribuição em lambdas é ilegal de qualquer maneira, então isso realmente não importa. Você pode usar a
exec()
função integrada para executar a atribuição de dentro do lambda, como neste exemplo:fonte
primeiro, você não precisa usar uma atribuição local para o seu trabalho, basta verificar a resposta acima
segundo, é simples usar locals () e globals () para obter a tabela de variáveis e, em seguida, alterar o valor
verifique este código de amostra:
se você precisar alterar a adição de uma variável global ao seu ambiente, tente substituir locals () por globais ()
A lista de comp do python é legal, mas a maior parte do projeto tridicional não aceita isso (como o flask: [)
espero que possa ajudar
fonte
locals()
, ele diz explicitamente na documentação que alterá-lo não altera realmente o escopo local (ou pelo menos nem sempre).globals()
por outro lado, funciona conforme o esperado.globals()
. pastebin.com/5Bjz1mR4 (testado em 2.6 e 3.2) prova isso.