Como sair de uma cláusula if

104

Que tipo de método existe para sair prematuramente de uma ifcláusula?

Há momentos em que estou escrevendo código e quero colocar uma breakinstrução dentro de uma ifcláusula, apenas para lembrar que eles só podem ser usados ​​para loops.

Vamos tomar o seguinte código como exemplo:

if some_condition:
   ...
   if condition_a:
       # do something
       # and then exit the outer if block
   ...
   if condition_b:
       # do something
       # and then exit the outer if block
   # more code here

Posso pensar em uma maneira de fazer isso: supondo que os casos de saída ocorram dentro de instruções if aninhadas, envolva o código restante em um grande bloco else. Exemplo:

if some_condition:
   ...
   if condition_a:
       # do something
       # and then exit the outer if block
   else:
       ...
       if condition_b:
           # do something
           # and then exit the outer if block
       else:
           # more code here

O problema com isso é que mais locais de saída significam mais código aninhado / indentado.

Como alternativa, eu poderia escrever meu código para que as ifcláusulas fossem as menores possíveis e não exigissem quaisquer saídas.

Alguém conhece uma maneira boa / melhor de sair de uma ifcláusula?

Se houver alguma cláusula else-if e else associada, eu acho que sair iria ignorá-la.

romano
fonte
2
Para o seu segundo exemplo de código - você conhece elif?
Craig McQueen
2
"Como alternativa, eu poderia escrever meu código para que as cláusulas if fossem as menores possíveis e não exigissem quaisquer saídas." - e certamente este seria o melhor curso de ação. :-)
Michał Marczyk
2
@Craig McQueen: Sim, mas digamos que eu queira que o código seja executado entre as instruções de condição? Por exemplo, if a: #stuff; #stuff_inbetween; if b: #stuff;o código intermediário depende, not amas não depende b.
Romano de
Olá, não se esqueça de elif stackoverflow.com/a/2069680/7045119
kerbrose

Respostas:

99

(Este método funciona para ifs, vários loops aninhados e outras construções que você não pode usar breakfacilmente.)

Envolva o código em sua própria função. Em vez de break, use return.

Exemplo:

def some_function():
    if condition_a:
        # do something and return early
        ...
        return
    ...
    if condition_b:
        # do something else and return early
        ...
        return
    ...
    return

if outer_condition:
    ...
    some_function()
    ...
Drew Dormann
fonte
4
Estou feliz em adicionar mais truques ao seu programador. Em minha experiência, essa abordagem funciona quase sempre que você fica tentado a usar um goto para avançar. (E tanto indica como aborda situações em que uma única função está ficando muito grande)
Drew Dormann
2
Idealmente, você pode alcançar ambos, mas há momentos em que você deve trocar um bom código por um bom desempenho. Esses momentos são raros, especialmente quando você está pensando em usar Python. Em outras palavras: não se preocupe tanto com a sobrecarga da chamada de função.
efemiente de
17
Há uma anedota antiga: "Dennis Ritchie encorajou a modularidade dizendo a todos que as chamadas de função eram muito, muito baratas em C. Todo mundo começou a escrever pequenas funções e modularizar. Anos depois, descobrimos que as chamadas de função ainda eram caras no PDP-11 , e o código VAX costumava gastar 50% do tempo na instrução CALLS. Dennis mentiu para nós! Mas era tarde demais; estávamos todos fisgados ... "
efêmero
1
@ephemient: Isso é engraçado, a história tem mais alguma coisa? Eu gostaria de ler tudo.
Romano de
4
Esta citação é do capítulo 4 do livro The Art of Unix Programming (online em faqs.org/docs/artu ). Você realmente deveria ler tudo, se não o leu antes.
efemiente de
55
from goto import goto, label

if some_condition:
   ...
   se condição_a:
       # faça alguma coisa
       # e então saia do bloco if externo
       goto .end
   ...
   se condição_b:
       # faça alguma coisa
       # e então saia do bloco if externo
       goto .end
   mais # código aqui

etiqueta .end

(Não use isso, por favor.)

efêmero
fonte
35
+1 porque isso é engraçado. Uma pesquisa no Google revelou para mim que este era um módulo de piadas do Dia da Mentira.
Romano de
2
Eu vinculei a ele também. Clique no primeiro goto.
efemiente de
1
isso me lembra o código assembly com todos os tipos de ramificação :)
phunehehe
2
@ephemient: Ah, não percebi o link. Achei que era o realce de código. Mas agora que vejo seu código, não vejo nenhum realce real acontecendo.
Romano
1
Quando o PHP introduziu Goto Virei-me para Python php.net/manual/en/control-structures.goto.php
Marc
25
while some_condition:
   ...
   if condition_a:
       # do something
       break
   ...
   if condition_b:
       # do something
       break
   # more code here
   break
Thomas Eding
fonte
3
Oh, ei, eu realmente gosto dessa ideia. Acho que isso resolve exatamente o meu desejo original. No entanto, tenho uma sensação incômoda de que essa não é uma boa prática (e, por esse motivo, vou manter a resposta aceita atualmente porque ela promove um bom estilo de codificação).
Romano de
6
Observe que você pode manter o original se e embrulhar tudo em um while True:. Apenas certifique-se de colocar uma breakdeclaração no final! Para línguas com a construção do-while, é mais idomaticamente fazer:do { code that can conditionally break out } while (false);
Thomas Eding
10

Você pode emular a funcionalidade de goto com exceções:

try:
    # blah, blah ...
    # raise MyFunkyException as soon as you want out
except MyFunkyException:
    pass

Isenção de responsabilidade: apenas quero chamar sua atenção para a possibilidade de fazer as coisas dessa maneira, embora de forma alguma endosso isso como razoável em circunstâncias normais. Como mencionei em um comentário sobre a questão, estruturar o código de modo a evitar as condicionais bizantinas em primeiro lugar é de longe preferível. :-)

Michał Marczyk
fonte
Haha, eu gosto dessa solução criativa. Embora eu respeite sua isenção de responsabilidade e não use esse código funky.
Romano de
@Roman: Fico feliz em adicionar outro truque ao lado do Shmoopty - mesmo quando me sinto malcriado em comparação. ;-)
Michał Marczyk
8

Talvez isto?

if some_condition and condition_a:
       # do something
elif some_condition and condition_b:
           # do something
           # and then exit the outer if block
elif some_condition and not condition_b:
           # more code here
else:
     #blah
if
ghostdog74
fonte
1
Sim, isso poderia funcionar. Acho que minha mente elifficou meio vazia enquanto eu escrevia isso. Embora eu ache que isso não funcionaria em uma situação em que desejo que o código seja executado entre as instruções if aninhadas.
Romano de
esta é realmente a resposta correta. por que não vejo pessoas recomendando isso ?? :)
Kerbrose
6

Para o que foi realmente perguntado, minha abordagem é colocar esses ifs dentro de um loop de um loop

while (True):
    if (some_condition):
        ...
        if (condition_a):
            # do something
            # and then exit the outer if block
            break
        ...
        if (condition_b):
            # do something
            # and then exit the outer if block
            break
        # more code here
    # make sure it is looped once
    break

Teste-o:

conditions = [True,False]
some_condition = True

for condition_a in conditions:
    for condition_b in conditions:
        print("\n")
        print("with condition_a", condition_a)
        print("with condition_b", condition_b)
        while (True):
            if (some_condition):
                print("checkpoint 1")
                if (condition_a):
                    # do something
                    # and then exit the outer if block
                    print("checkpoint 2")
                    break
                print ("checkpoint 3")
                if (condition_b):
                    # do something
                    # and then exit the outer if block
                    print("checkpoint 4")
                    break
                print ("checkpoint 5")
                # more code here
            # make sure it is looped once
            break
izzulmakin
fonte
1
Para ser honesto, essa é a solução mais limpa. É muito claro quando o código deve ser encerrado - você não precisa se preocupar com o escopo das funções dentro das funções e não há desempenho ou débito lógico.
Trent de
2
Pode ser considerado usar em for _ in range(1):vez de while True:. (1) Comunique melhor sua intenção de um único loop de iteração e (2) nenhuma instrução de última interrupção para sair do loop (pode ser removido acidentalmente mais tarde)
Bernhard Kausler
3

De um modo geral, não. Se você está aninhando "se" e rompendo com eles, está fazendo errado.

No entanto, se você deve:

if condition_a:
   def condition_a_fun():
       do_stuff()
       if we_wanna_escape:
           return
   condition_a_fun()
if condition_b:
   def condition_b_fun():
       do_more_stuff()
       if we_wanna_get_out_again:
           return
   condition_b_fun()

Observe que as funções não PRECISAM ser declaradas na instrução if, elas podem ser declaradas antecipadamente;) Esta seria uma escolha melhor, pois evitará a necessidade de refatorar um if / then feio posteriormente.

Enki
fonte
Obrigado pela resposta. Não tenho certeza do que você entende por 'loops de aninhamento'. Se você está se referindo à minha menção à palavra-chave 'quebra', eu estava simplesmente tentando motivar minha busca por uma saída if comparando-a com a existência de uma saída em loop. Além disso, não tenho certeza de como seu código resolve o problema, como meu exemplo tinha if condition_ae if condition_baninhado dentro de um if some_condition. Eu quero ser capaz de sair do if some_condition.
Romano de
O motivo pelo qual as pessoas desejam aninhar ifs e quebrá-los provavelmente não é porque estão fazendo isso errado, mas talvez porque desejam escrever código limpo simples e DRY. O caso simples é quando o programa precisa fazer x () quando condição_a e condição_b são satisfeitas, e precisa fazer y () apenas para condição_a, e precisa fazer z () apenas para condição_b. e o codificador se recusa a escrever x () várias vezes
izzulmakin
1

Efetivamente, o que você está descrevendo são instruções goto, que geralmente são bastante deslocadas. Seu segundo exemplo é muito mais fácil de entender.

No entanto, mais limpo ainda seria:

if some_condition:
   ...
   if condition_a:
       your_function1()
   else:
       your_function2()

...

def your_function2():
   if condition_b:
       # do something
       # and then exit the outer if block
   else:
       # more code here
Smashery
fonte
1

Há outra maneira que não depende da definição de funções (porque às vezes é menos legível para pequenos trechos de código), não usa um loop while externo extra (que pode precisar de uma apreciação especial nos comentários para ser compreensível à primeira vista) , não usa goto (...) e o mais importante, permite que você mantenha seu nível de indentação para o externo, caso não precise iniciar o aninhamento.

if some_condition:
   ...
   if condition_a:
       # do something
       exit_if=True # and then exit the outer if block
if some condition and not exit_if: # if and only if exit_if wasn't set we want to execute the following code
   # keep doing something
   if condition_b:
       # do something
       exit_if=True # and then exit the outer if block
if some condition and not exit_if:
   # keep doing something

Sim, isso também precisa de uma segunda olhada para legibilidade, no entanto, se os trechos de código forem pequenos, isso não exige o rastreamento de nenhum loop while que nunca se repita e depois de entender para que servem os ifs intermediários, é facilmente legível, tudo em um lugar e com o mesmo recuo.

E deve ser muito eficiente.

DonQuiKong
fonte
0

Então, aqui eu entendo que você está tentando sair do bloco de código if externo

if some_condition:
    ...
    if condition_a:
       # do something
       # and then exit the outer if block
       ...
    if condition_b:
       # do something
       # and then exit the outer if block
# more code here

Uma maneira de sair disso é que você pode testar se há uma condição falsa no bloco if externo, que então sairá implicitamente do bloco de código, você então usa um bloco else para aninhar os outros ifs para fazer algo

if test_for_false:
    # Exit the code(which is the outer if code)

else:
    if condition_a:
        # Do something

    if condition_b:
        # Do something
Romeo
fonte
0

A única coisa que poderia aplicar isso sem métodos adicionais é elifcomo o exemplo a seguir

a = ['yearly', 'monthly', 'quartly', 'semiannual', 'monthly', 'quartly', 'semiannual', 'yearly']
# start the condition
if 'monthly' in b: 
    print('monthly') 
elif 'quartly' in b: 
    print('quartly') 
elif 'semiannual' in b: 
    print('semiannual') 
elif 'yearly' in b: 
    print('yearly') 
else: 
    print('final') 
Querbrose
fonte
-1

Esta é outra maneira de lidar com isso. Ele usa um único item para loop que permite que você use apenas continue. Ele evita a necessidade desnecessária de funções extras sem motivo. E, adicionalmente, elimina possíveis loops while infinitos.

if something:
    for _ in [0]:
        # Get x
        if not x:
            continue

        # Get y
        if not y:
            continue

        # Get z
        if not z:
            continue

        # Stuff that depends on x, y, and z
Timbot
fonte
-2

usar returnna condição if retornará você da função, para que você possa usar return para interromper a condição if.

Nikhil Parashar
fonte
2
ele quer interromper o senão sair da função
StealthOne