Как выйти из предложения if


104

Какие существуют способы преждевременного выхода из ifпредложения?

Бывают случаи, когда я пишу код и хочу поместить breakоператор внутри ifпредложения, только чтобы помнить, что его можно использовать только для циклов.

В качестве примера возьмем следующий код:

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

Я могу придумать один способ сделать это: если предположить, что случаи выхода происходят во вложенных операторах if, оберните оставшийся код в большой блок else. Пример:

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

Проблема заключается в том, что большее количество мест выхода означает большее количество вложений / отступов кода.

В качестве альтернативы я мог бы написать свой код, чтобы ifпредложения были как можно меньше и не требовали никаких выходов.

Кто-нибудь знает о хорошем / лучшем способе выхода из ifпункта?

Если есть какие-либо связанные предложения else-if и else, я полагаю, что при выходе они будут пропущены.


2
Для вашего второго примера кода - вы знаете elif?
Craig McQueen

2
«В качестве альтернативы, я мог бы написать свой код так, чтобы предложения if были как можно меньше и не требовали никаких выходов». - и, конечно же, это было бы лучшим курсом действий. :-)
Michał Marczyk

2
@Craig McQueen: Да, но я хочу, чтобы код выполнялся между операторами условий? Например, if a: #stuff; #stuff_inbetween; if b: #stuff;промежуточный код зависит от, not aно не зависит от b.
Роман

привет, пожалуйста, не забывайте elif stackoverflow.com/a/2069680/7045119
Kerbrose

Ответы:


99

(Этот метод работает для ifs, нескольких вложенных циклов и других конструкций, с которыми вам не breakлегко справиться.)

Оберните код в отдельную функцию. Вместо breakиспользования return.

Пример:

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()
    ...

4
Я с радостью пополню ваш набор уловок программиста. По моему опыту, этот подход работает почти каждый раз, когда возникает соблазн использовать прямой переход. (И это одновременно намекает на ситуации, когда одна функция становится слишком большой, и обращается к ним)
Дрю Дорманн

2
В идеале вы можете добиться и того, и другого, но бывают случаи, когда вы должны обменять хороший код на хорошую производительность. Такие случаи редки, особенно когда вы думаете об использовании Python. Другими словами: не беспокойтесь о накладных расходах на вызов функций.
ephemient

17
Есть старый анекдот: «Деннис Ритчи поощрял модульность, говоря всем и каждому, что вызовы функций были действительно очень дешевы в C. Все начали писать небольшие функции и разбивать их на модули. Спустя годы мы обнаружили, что вызовы функций все еще были дорогими на PDP-11. , и код VAX часто тратил 50% своего времени на инструкцию CALLS. Деннис солгал нам! Но было слишком поздно; мы все были на крючке ... »
ephemient

1
@ephemient: Забавно, есть еще что-нибудь в этой истории? Я бы хотел прочитать все это.
Роман

4
Это цитата из главы 4 книги Искусство программирования Unix (на сайте faqs.org/docs/artu ). Вам действительно стоит прочитать это целиком, если вы не читали раньше.
ephemient

55
from goto import goto, label

если some_condition:
   ...
   если condition_a:
       # сделай что-нибудь
       # а затем выходим из внешнего блока if
       goto .end
   ...
   если condition_b:
       # сделай что-нибудь
       # а затем выходим из внешнего блока if
       goto .end
   # еще код здесь

label .end

(Не используйте это, пожалуйста.)


35
+1 потому что это смешно. Поиск в Google показал мне, что это был модуль первоапрельской шутки.
Роман

2
Я тоже связался с ним. Щелкните по первому goto.
ephemient

1
это напоминает мне ассемблерный код со всеми видами ветвления :)
phunehehe

2
@ephemient: Ах, не заметил ссылку. Думал, это подсветка кода. Но теперь, когда я смотрю на ваш код, я не вижу никакого реального выделения ...
Роман

1
Когда PHP представил goto, я обратился к 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

3
О, эй, мне очень нравится эта идея. Думаю, это как раз и решает мое изначальное желание. Однако у меня есть неприятное ощущение, что это не очень хорошая практика (и по этой причине я пока сохраню текущий принятый ответ, потому что он способствует хорошему стилю кодирования).
Роман

6
Обратите внимание, что вы можете сохранить оригинал if и обернуть все это в файл while True:. Просто не забудьте написать breakзаявление в конце! Для языков с конструкцией do-while это более идоматично:do { code that can conditionally break out } while (false);
Томас Эдинг,

10

Вы можете эмулировать функциональность goto с исключениями:

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

Заявление об ограничении ответственности: я лишь хочу обратить ваше внимание на возможность такого поведения, но никоим образом не считаю это разумным при нормальных обстоятельствах. Как я уже упоминал в комментарии к этому вопросу, структурирование кода таким образом, чтобы в первую очередь избегать византийских условных выражений, безусловно, предпочтительнее. :-)


Ха-ха, мне нравится это креативное решение. Хотя я буду соблюдать ваш отказ от ответственности и не буду использовать такой напуганный код.
Роман

@Roman: Я счастлив добавить еще один трюк вместе с Шмоопти - даже когда я чувствую себя непослушным по сравнению. ;-)
Michał Marczyk

8

Может быть, это?

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

1
Да, это могло сработать. Я думаю, что elifкогда я писал это, мой разум был отключен . Хотя я думаю, что это не сработает в ситуации, когда я хочу, чтобы код выполнялся между вложенными операторами if.
Роман

это действительно правильный ответ. почему я не вижу, чтобы люди рекомендовали это ?? :)
kerbrose

6

Что касается того, о чем на самом деле спрашивали, мой подход состоит в том, чтобы поместить их ifв один цикл

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

Попробуй это:

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

1
Если честно, это самое чистое решение. Совершенно ясно, когда код должен завершиться - вам не нужно беспокоиться о масштабировании функций внутри функций, а также нулевой производительности или логической задолженности.
Трент

2
Можно подумать об использовании for _ in range(1):вместо while True:. (1) Лучше сообщить о своем намерении создать единый итерационный цикл и (2) нет последнего оператора break для выхода из цикла (позже может быть случайно удален)
Бернхард

3

Вообще говоря, не надо. Если вы вкладываете «если» и отрываетесь от них, вы делаете это неправильно.

Однако, если вам необходимо:

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()

Обратите внимание: функции НЕ ДОЛЖНЫ быть объявлены в операторе if, их можно объявить заранее;) Это было бы лучшим выбором, так как он избавит вас от необходимости рефакторинга уродливого if / then позже.


Спасибо за ответ. Я не совсем понимаю, что вы подразумеваете под «петлями вложенности». Если вы имеете в виду мое упоминание ключевого слова «break», я просто пытался мотивировать свой поиск if-exit, сравнивая его с существованием выхода из цикла. Кроме того , я не уверен , как ваш код решает эту проблему, так как мой пример был if condition_aи if condition_bвложенным внутри if some_condition. Я хочу иметь возможность вырваться из if some_condition.
Роман

Причина, по которой люди хотят вкладывать if и нарушать их, вероятно, не в том, что они делают это неправильно, а в том, что они хотят писать чистый простой и СУХОЙ код. простой случай - когда программе нужно выполнить x (), когда выполняются и condition_a, и condition_b, и нужно выполнять y () только для condition_a, и нужно выполнять z () только для condition_b. и кодировщик отказывается писать x () несколько раз
izzulmakin

1

По сути, то, что вы описываете, - это операторы goto, которые обычно довольно сильно панорамируются. Ваш второй пример гораздо легче понять.

Однако чище все равно будет:

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

1

Есть другой способ, который не полагается на определение функций (потому что иногда это менее читабельно для небольших фрагментов кода), не использует дополнительный внешний цикл while (который может потребовать особого внимания в комментариях, чтобы его можно было понять с первого взгляда) , не использует goto (...) и, что наиболее важно, позволяет вам сохранить свой уровень отступа для внешнего, если вам не нужно начинать вложение.

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

Да, это также требует второго взгляда на удобочитаемость, однако, если фрагменты кода небольшие, это не требует отслеживания каких-либо циклов while, которые никогда не будут повторяться, и после понимания того, для чего нужны промежуточные if, он легко читается, все в одно место и с таким же отступом.

И это должно быть довольно эффективно.


0

Итак, я понимаю, что вы пытаетесь вырваться из внешнего блока кода if

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

Одним из выходов из этого является то, что вы можете проверить наличие ложного условия во внешнем блоке if, который затем неявно выйдет из блока кода, затем вы используете блок else, чтобы вложить другие if, чтобы что-то сделать

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

else:
    if condition_a:
        # Do something

    if condition_b:
        # Do something

0

Единственное, что можно применить без дополнительных методов, - elifэто следующий пример.

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') 

-1

Вот еще один способ справиться с этим. Он использует один элемент цикла for, который позволяет вам просто использовать continue. Это предотвращает ненужную потребность в дополнительных функциях без причины. И дополнительно исключает потенциальные бесконечные циклы while.

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

-2

использование returnв условии if вернет вас из функции, так что вы можете использовать return для разрыва условия if.


2
он хочет сломать если не выйти из функции
StealthOne
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.