Eu tenho um método que chama 4 outros métodos em sequência para verificar condições específicas e retorna imediatamente (não verificando os seguintes) sempre que alguém retorna algo Truthy.
def check_all_conditions():
x = check_size()
if x:
return x
x = check_color()
if x:
return x
x = check_tone()
if x:
return x
x = check_flavor()
if x:
return x
return None
Parece muito código de bagagem. Em vez de cada declaração if de 2 linhas, prefiro fazer algo como:
x and return x
Mas isso é Python inválido. Estou perdendo uma solução simples e elegante aqui? Aliás, nessa situação, esses quatro métodos de verificação podem ser caros, então não quero chamá-los várias vezes.
python
if-statement
Bernard
fonte
fonte
x and return x
é melhor do queif x: return x
? O último é muito mais legível e, portanto, sustentável. Você não deve se preocupar muito com o número de caracteres ou linhas; legibilidade conta. Eles são exatamente o mesmo número de caracteres que não são espaços em branco e, se você realmente precisar,if x: return x
funcionará bem em apenas uma linha.x
(ao contrário debool(x)
) assim como está, acho que é seguro assumir que as funções do OP podem retornar qualquer coisa, e ele quer o primeiro qualquer coisa que seja verdadeira.Respostas:
Você pode usar um loop:
Isso tem a vantagem adicional de que agora você pode tornar o número de condições variáveis.
Você pode usar
map()
+filter()
(as versões do Python 3, use asfuture_builtins
versões do Python 2) para obter o primeiro valor correspondente:mas se for mais legível, é discutível.
Outra opção é usar uma expressão geradora:
fonte
any
vez do loop.return any(condition() for condition in conditions)
any
tem quase a mesma implementação dentro. Mas parece muito melhor, por favor, postá-lo como uma resposta)conditions
earguments
? Isso é muito pior do que o código original, que leva cerca de 10 segundos para analisar pelo meu analisador cerebral.return next((check for check in checks if check), None)
.Como alternativa à boa resposta de Martijn, você pode encadear
or
. Isso retornará o primeiro valor de verdade, ouNone
se não houver valor de verdade:Demo:
fonte
\
para colocar cada verificação em sua própria linha.\
para estender a linha lógica. Use parênteses sempre que possível; assimreturn (....)
com as novas linhas inseridas conforme necessário. Ainda assim, essa será uma longa linha lógica.Não mude
Existem outras maneiras de fazer isso, como mostram as várias outras respostas. Nenhum é tão claro quanto o seu código original.
fonte
:
, porque consideroif x: return x
muito bem, e isso torna a função mais compacta. Mas isso pode ser apenas eu.or
como timgeb fez é um idioma adequado e bem compreendido. Muitas línguas têm isso; talvez, quando chamadoorelse
, seja ainda mais claro, mas até o mais antigoor
(ou||
em outras línguas) deve ser entendido como a alternativa para tentar se o primeiro "não funciona".or
não-pitonico ou de alguma forma ofuscado, mas isso é uma questão de opinião de qualquer maneira - como deveria ser.Efetivamente, a mesma resposta que timgeb, mas você pode usar parênteses para obter uma formatação melhor:
fonte
De acordo com a lei de Curly , você pode tornar esse código mais legível dividindo duas preocupações:
em duas funções:
Isso evita:
... preservando um fluxo linear e fácil de ler.
Você provavelmente também pode criar nomes de funções ainda melhores, de acordo com sua circunstância específica, o que o torna ainda mais legível.
fonte
return None
não é necessário, porque as funções retornamNone
por padrão. No entanto, não há nada errado em retornarNone
explicitamente, e eu gosto que você tenha escolhido fazê-lo.Esta é uma variante do primeiro exemplo de Martijns. Ele também usa o estilo "coleção de callables" para permitir um curto-circuito.
Em vez de um loop, você pode usar o builtin
any
.Observe que
any
retorna um booleano; portanto, se você precisar do valor exato de retorno da verificação, esta solução não funcionará.any
não fará distinção entre14
,'red'
,'sharp'
,'spicy'
como valores de retorno, todos eles serão devolvidos comoTrue
.fonte
next(itertools.ifilter(None, (c() for c in conditions)))
para obter o valor real sem convertê-lo em um booleano.any
realmente curto-circuito?x = bar(); if x: return x;
Você já pensou em escrever
if x: return x
tudo em uma linha?Isso não é menos repetitivo do que você tinha, mas IMNSHO parece um pouco mais suave.
fonte
Estou surpreso que ninguém tenha mencionado o built-in
any
que é feito para esse fim:Observe que embora essa implementação seja provavelmente a mais clara, ela avalia todas as verificações, mesmo que a primeira seja
True
.Se você realmente precisar parar na primeira falha na verificação, considere usar o
reduce
que é feito para converter uma lista em um valor simples:No seu caso:
lambda a, f: a or f()
é a função que verifica se o acumuladora
ou a verificação atualf()
éTrue
. Observe que sea
forTrue
,f()
não será avaliado.checks
contém funções de verificação (of
item do lambda)False
é o valor inicial, caso contrário, nenhuma verificação aconteceria e o resultado seria sempreTrue
any
ereduce
são ferramentas básicas para programação funcional. Convido você a treiná-los e também omap
que é incrível!fonte
any
só funciona se as verificações realmente retornarem um valor booleano, literalmenteTrue
ouFalse
, mas a pergunta não especificar isso. Você precisaria usarreduce
para retornar o valor real retornado pela verificação. Além disso, é fácil evitar avaliar todas as verificaçõesany
usando um gerador, por exemploany(c() for c in (check_size, check_color, check_tone, check_flavor))
. Como na resposta de Leonhardreduce
. Como @DavidZ, acredito que sua soluçãoany
deve usar um gerador e é preciso salientar que se limita a retornarTrue
ouFalse
.any
trabalha com valores de verdade :any([1, "abc", False]) == True
eany(["", 0]) == False
any
só funciona para esse propósito se valores booleanos reais forem retornados das funções de verificação.Se você quiser a mesma estrutura de código, poderá usar declarações ternárias!
Eu acho que isso parece bom e claro, se você olhar para ele.
Demo:
fonte
x if x else <something>
só pode ser reduzido para #x or <something>
Para mim, a melhor resposta é a do @ phil-frost, seguida pelo @ wayne-werner's.
O que acho interessante é que ninguém disse nada sobre o fato de que uma função retornará muitos tipos de dados diferentes, o que tornará obrigatório fazer verificações no tipo de x em si para realizar qualquer trabalho adicional.
Então, eu misturaria a resposta do @ PhilFrost com a idéia de manter um único tipo:
Observe que isso
x
é passado como argumento, mas tambémall_conditions
é usado como um gerador passado de funções de verificação, onde todas elas recebem umax
verificação e retornamTrue
ouFalse
. Usandofunc
comall_conditions
o valor padrão, você pode usarassessed_x(x)
, ou você pode passar um gerador de ainda mais personalizado viafunc
.Dessa forma, você recebe
x
assim que um cheque passa, mas sempre será do mesmo tipo.fonte
Idealmente, eu reescreveria as
check_
funções para retornarTrue
ou, emFalse
vez de um valor. Seus cheques então se tornamSupondo que você
x
não seja imutável, sua função ainda poderá modificá-la (embora não possa reatribuí-la) - mas uma função chamadacheck
não deve realmente modificá-la de qualquer maneira.fonte
Uma pequena variação no primeiro exemplo de Martijns acima, que evita o se dentro do loop:
fonte
Status or c()
irá pular / causar um curto-circuito nas chamadas de avaliaçãoc()
seStatus
for verdade, de modo que o código nesta resposta não pareça chamar mais funções do que o código do OP. stackoverflow.com/questions/2580136/…Eu gosto de @ timgeb's. Enquanto isso, gostaria de acrescentar que a expressão
None
nareturn
declaração não é necessária, pois a coleção deor
instruções separadas é avaliada e a primeira zero-zero, nenhuma-vazia, nenhuma-Nenhuma é retornada e, se não houver nenhuma,None
é retornada. se existeNone
ou não!Então, minha
check_all_conditions()
função é assim:Usando
timeit
comnumber=10**7
eu olhei para o tempo de execução de várias sugestões. Para fins de comparação, usei arandom.random()
função para retornar uma string ou comNone
base em números aleatórios. Aqui está o código inteiro:E aqui estão os resultados:
fonte
Dessa forma, é um pouco fora da caixa, mas acho que o resultado final é simples, legível e com boa aparência.
A idéia básica é
raise
uma exceção quando uma das funções é avaliada como verdadeira e retorna o resultado. Aqui está como isso pode parecer:Você precisará de uma
assertFalsey
função que crie uma exceção quando um dos argumentos de função chamados for avaliado como verdadeiro:O exposto acima pode ser modificado para fornecer também argumentos para as funções a serem avaliadas.
E é claro que você precisará do
TruthyException
próprio. Esta exceção fornece oobject
que acionou a exceção:Você pode transformar a função original em algo mais geral, é claro:
Isso pode ser um pouco mais lento porque você está usando uma
if
instrução e manipulando uma exceção. No entanto, a exceção é tratada apenas no máximo uma vez, portanto, o impacto no desempenho deve ser menor, a menos que você espere executar a verificação e obter umTrue
valor muitos milhares de vezes.fonte
StopIteration
é um bom exemplo: uma exceção é gerada toda vez que você esgotar um iterável. O que você deseja evitar é gerar sucessivas exceções repetidas vezes, o que seria caro. Mas fazer isso uma vez não é.A maneira pitônica é usar o comando reduzir (como alguém já mencionado) ou ferramentas (como mostrado abaixo), mas parece-me que o simples uso do curto-circuito do
or
operador produz um código mais clarofonte
Vou pular aqui e nunca escrevi uma única linha de Python, mas presumo que
if x = check_something(): return x
seja válido?se então:
fonte
if ( x := check_size() ) :
para o mesmo efeito.Ou use
max
:fonte
Eu já vi algumas implementações interessantes de declarações de troca / caso com ditados no passado que me levaram a essa resposta. Usando o exemplo que você forneceu, você obteria o seguinte. (É loucura
using_complete_sentences_for_function_names
, entãocheck_all_conditions
é renomeado parastatus
. Veja (1))A função de seleção elimina a necessidade de chamar cada
check_FUNCTION
duas vezes, ou seja, você evitacheck_FUNCTION() if check_FUNCTION() else next
adicionando outra camada de função. Isso é útil para funções de execução longa. As lambdas no ditado atrasam a execução de seus valores até o loop while.Como bônus, você pode modificar a ordem de execução e até pular alguns dos testes alterando
k
e,s
por exemplo,k='c',s={'c':'b','b':None}
reduzindo o número de testes e revertendo a ordem de processamento original.o
timeit
bolsistas podem pechinchar sobre o custo de adicionar uma ou duas camadas extras à pilha e o custo do ditado procurar, mas você parece mais preocupado com a beleza do código.Como alternativa, uma implementação mais simples pode ser a seguinte:
fonte
check_no/some/even/prime/every_third/fancy_conditions
mas apenas essa função; por que não chamá-lostatus
ou se alguém insistircheck_status
. Usar_all_
é supérfluo, ele não garante a integridade dos universos. A nomeação certamente deve usar um conjunto consistente de palavras-chave, aproveitando o espaçamento de nomes sempre que possível. Frases longas servem melhor como doutrinas. Raramente é necessário mais de 8 a 10 caracteres para descrever algo de forma sucinta.check_all_conditions
é um nome ruim, porque é não verificar todas as condições, se uma é verdadeira. Eu usaria algo comomatches_any_condition
.