0,0
рейтинг
24 июля 2012 в 12:41

Разработка → Прочие варианты использования оператора else перевод

Всем нам хорошо известен способ использования ключевого слова else совместно с if:

if x > 0: 
    print 'positive' 
elif x < 0: 
    print 'negative' 
else: 
    print 'zero' 


Однако в Python’е существует и несколько других, неизвестных большинству программистов, применений else.


for… else


Бьюсь об заклад, что вам неизвестно, что else можно ставить сразу после окончания цикла for! Для чего, спросите вы? Когда элементы перебираемой последовательности будут исчерпаны, управление перейдет коду, находящемуся в блоке else. «А когда эти элементы не будут исчерпаны?» — когда вы выйдете из цикла досрочно по условию break.

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

broken = False 
for person in people: 
    person.use(computer) 
    if computer.is_broken: 
        broken = True 
        break 
if not broken: 
    print 'The computer is fine!' 


С использованием for..else мы можем привести этот код к более удобочитаемой форме:

for person in people: 
    person.use(computer) 
    if computer.is_broken: 
        break 
else: 
    print 'The computer is fine!' 


while… else


Семантика здесь абсолютно та же, что и в предыдущем случае. Тело цикла while выполняется до тех пор, пока верно некое условие — это вы и так уже знаете. Если в какой-то момент условие перестало быть верным, выполнение переходит в ветвь else. Оператор break, если условие дойдет до него, прервет выполнение всего цикла while..else, таким образом, ветвь else выполняться не будет. В двух словах: логика поведения while..else точно такая же, как и у for..else.

while usage < 10 and person.want_to_play: 
    person.use(computer) 
    if computer.broken: 
        break 
else: 
    print 'The computer is fine!' 


try… except… else


Есть некоторый код, заключенный в блоке try. Вы хотите перехватывать определенные исключения, и обрабатывать их, но что, если в процессе выполнения никаких исключений выброшено не было? На помощь приходит условие else. Оно будет выполняться лишь в том случае, когда никаких исключения выброшено не было, и перед блоком finally (если он, конечно, существует). Нужно заметить, что исключения, порожденные в блоке else предшествующими конструкциями except, обрабатываться не будут:

 def get_person(people):
     try:
         person = people[3]
     except IndexError:
         person = None
     else:
         person.do_work()
     return person 


Управление дойдет до ветви else, если иключение IndexError не было выброшено. Почему это может быть полезным? Мы можем внести person.do_work() в блок try, но что произойдет, если do_work() в процессе работы выбросит исключение IndexError? В таком случае, оно будет поймано нашим блоком except, результаты чего могут быть катастрофическими (если конечно мы не приготовились к этому заранее — в таком случае, IndexError, порожденный do_work(), будет проброшен далее, как и происходит в нашей функции get_person(person)).

Заключение


Честно говоря, я не нашел особой пользы от применения оператора else вне связки с if. Я считаю, что в большинстве случаев не следует использовать его вместе с операторами циклов, так как получающиеся конструкции являются не интуитивными (хотя им можно дать “зеленый свет” в ситуациях, когда это приводит к более краткому и удобочитаемому коду). С другой стороны, использование else совместно с try куда как более интуитивно, и, возможно, представляет собой лучшую альтернативу отлавливанию тех исключений, которые нужно пробросить дальше, или использованию флаговой переменной, позволяющей понять, было ли выброшено исключение в процессе работы блока try..except.
Перевод: Amir Rachum
Артём Гапченко @artemgapchenko
карма
43,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (52)

  • +20
    Я думаю эти варианты использования известны всем кто программирует на Python. Неизвестны они лишь для тех кто только пробует использовать Python и пишет в стилистике Java / C#.
    • +28
      Я знаю одного мужчину, друг которого не знал. =) А если серьёзно, то новички скажут спасибо.
      • 0
        О да, коллега по работе одного моего знакомого тоже не знал и с интересом прочитал эту статью.
    • 0
      Ну, я Python года полтора-два назад изучать начал. Похоже, что эти варианты использования else я тогда пропустил мимо ушей, так как за прошедшее время я к Python'у несколько раз возвращался, кой-чего на нем писал, но про них узнал только сегодня. :)
    • +1
      Вот последнее время немного Python'ю. Зарабатываю деньги себе на C#/Java/PHP. Про интересные варианты использования else не слышал. Спасибо.
  • +25
    когда я первый раз увидел for… else, то всё же ожидал, что else исполняется если цикл не сделал ни одной итерации — имхо это куда более ожидаемое поведение
    • +1
      Это было бы как раз неожиданным поведением. Ведь по существу в Питоне цикл for это try...except блок, в котором внутри while True: происходит последовательный вызов iterator.__next__(), а перехватывается исключение StopIteration. Команда break, как и положено, выкидывает из блока try/except/else, выполняя только содержимое блока finally. То есть всё по уму — содержимое else в for...else выполняется всегда, кроме случая явного вызова break (ну или ещё снаружи try...except навесили).
      • –11
        вот же ж как оно ж… что можно сделать из простого while… навесить исключениев, перехватчиков и потом думай-гадай, что ж оно делает на самом деле.

        еще в школе учили, что break == goto, а goto == попа
        а тут не только break, да еще все, что написано чуть выше, прям винегрет.
        и оно еще и работает?! :))
        • +2
          Плохо вас в школе учили, break как раз и придумали как замену goto
          • 0
            О, да, какой же я тупой…
            Брейк это явный переход к метке за пределом цикла если что.
            Это не goto наверное… нет, что вы, совсем нет!
            • –2
              Но я в самом деле даже не против правильно использованного goto, и совсем не против break, который и хотели сделать как правильный goto. Но обертка этого всего в обработку исключений как штатного прекращения цикла — это для меня нонсенс.
              • 0
                Ещё для справки: Питон вообще почти весь на исключениях держится.
        • 0
          switch тогда еще больший goto. А switch в С больший goto чем в Pascal, например :)

          Goto никуда не делся, его просто припрятали в другие конструкции
          • 0
            «switch» в С — это вообще песня, прерываемая break'ами :)
            а в паскале «case» как-то вполне органично смотрится на мой взгляд
            • 0
              Да, case d паскале и лаконичнее и удобнее. Очень удобна возможность указать диапазон или список значений:

              case value of
              • 0
                case value of
                1..3,5: doSomethig();
                4: doSomethingElse();
                else
                  ....
                end;
      • +8
        Это всё должен знать человек, который «первый раз увидел for… else»?
        • +2
          Cудя по всему теперь оно так… если раньше я как-то думал, что цикл выглядит примерно как в асме и там все соптимизировано до предела, то перерь оказывается все разоптимизировано, что фиг поймешь, где надо экономить, а где нет; где раньше был проверенный код, теперь при простом переводе на другой язык программирования настолько все внутри усложняется, что делается как-то нехорошо :( одно выбрасывание исключений чего стоит — это же раскрутка стека и прочие накладные расходы.
          Вобщем-то этот, казалось бы невинный, комментарий, как оно внутри устроено, вот так враз поменял мое мнение о питоне.
          • 0
            Суть питона как раз в том что на мелочах можно не экономить ради удобства программиста. Для экономии пишут на более низкоуровневых языках.
          • 0
            Просто для справки: исключения в Питоне работают быстро, потому что всё происходит внутри интерпретатора.
            • +4
              … а интерпретируемый код по определению работает медленно :)
    • +1
      Вот мне тоже такое поведение кажется единственно возможным.
      Else = иначе. Выполни такой-то блок кода, а иначе (если не получилось) — выполни секцию else. В писоне же получается «выполни и тело цикла, и блок else» — че за бред?
  • –3
    Честно говоря, я не нашел особой пользы от применения оператора else вне связки с if.

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

    Для начинающих писать на Python статья очень полезная.
    • +8
      Хм, согласно предыдущему комментарию это совсе-совсем неочевидно с точностью до наоборот. Так что лично мое мнение — вообще не использовать такие конструкции, которые могут так двояко толковаться — от греха подальше.
  • +1
    Во всех манах по питону которые я читал использование else с for и прочими были описаны если не в первых страницах, то в первой главе точно.
    • +2
      Совершенно верно.
      Я понимаю, из-за недостатка опыта можно недостаточно свободно ориентироваться в стандартной библиотеке или в более продвинутых возможностях, но не знать таких возможностей ключевых управляющих структур языка == не знать самого языка, как мне кажется.
      >Бьюсь об заклад, что Вам неизвестно, что else можно ставить сразу после окончания цикла for!
      Это утверждение весьма спорно.
      • +1
        но не знать таких возможностей ключевых управляющих структур языка == не знать самого языка, как мне кажется.

        Я вот про for...else и while...else, может, и знал, но точно не помнил. Потому что они, в отличие от try/except/else, не то чтобы часто нужны. Да, установить флаг и посмотреть после цикла его значение, конечно, часто случается, но как только он устанавливается по правилам чуть сложнее, чем «установить в False и выйти», так сразу эта конструкция не работает. Или другая ситуация: флаг-то установили, но это далеко не всегда означает, что не надо сделать ещё что-то в других итерациях.
    • 0
      Книги по теории читать сейчас немодно, намного выгодней сразу копипастить и быдлокодить!
  • +3
    Не пойму как это может быть неизвестно любому, кто пишет на python и прочитал хотя бы вводные материалы. Или это не принято в наше время? O_o
  • +4
    Бьюсь об заклад, что Вам неизвестно, что else можно ставить сразу после окончания цикла for!
    Проиграли. Много заложили? Кстати, зачем тут «вам» с большой? Вы не обращаетесь к конкретному лицу, перед вами аудитория читателей.
    • +1
      Кстати, зачем тут «вам» с большой? Вы не обращаетесь к конкретному лицу, перед вами аудитория читателей.

      Спасибо за замечание, поправил.
      Проиграли. Много заложили?

      Эти остроты лучше будет адресовать автору оригинального текста. Я — всего лишь переводчик.
  • +1
    я рад, что этого нет в java))
  • –1
    Вот каждый раз как читаю «синтаксический сахар» — глаз спотыкается. Интересно, никого больше не раздражает это словосочетание?
    • +1
      Очень уж ГМО напоминает :)
      • +1
        И обе вещи крайне полезны!
        ЗЫ. не стоит придираться к терминам, тем более это калька. Предложите лучше и если будет лучше то приживется
  • 0
    а я вот только не давно узнал, правда с топика на stackoverflow'e, там еще несколько интересных возможностей описано
    • +2
      ИЧСХ, три плашки: locked, closed as not constructive, protected.

      Я ненавижу модераторов StackOverflow.
  • +2
    Использование else вместе с циклами мне очень понравилось в движке шаблонов Jinja 2. Например:
    <ul>
    {% for user in users %}
        <li>{{ user.username|e }}</li>
    {% else %}
        <li><em>no users found</em></li>
    {% endfor %}
    </ul>
    

    Как по мне, гораздо читабельнее, что проверка длины списка users. А если в users хранится генератор, то без else сильно туго придётся.
    • 0
      В Django такое-же поведение. Как-то более логичным кажется :)
  • 0
    Спасибо за статью. Я только начал изучение Пайтона, а до него четверть века программировал на разных языках, и такая фича действительно для меня приятная неожиданность. Все таки Пайтон — это сила.
  • 0
    На питоне почти не писал, и про это не знал, интересная особенность. Еще нравится короткий elif (где-то еще его встречал, в ruby что ли...). Жаль еще, что некоторые языки не знают про?:. Вот бы все такие плюшки едино собрать)
    • 0
      Насчёт оператора ?: в Python есть аналог ([1], PEP-308): A if B else C
      Синтаксис выбрали в стиле Python: поменьше специальных символов и «Читаемость имеет значение».
    • 0
      В перле есть короткий «elsif» если что, собирайте в копилку ;)
  • 0
    Так вроде Марк Лутц описывал это всё очень подробно, не?
    • 0
      Настолько подробно, что многие боятся открывать его двухтомник :)
  • 0
    for person in people: 
        person.use(computer) 
        if computer.is_broken: 
            break 
    else: 
        print 'The computer is fine!' 
    


    Я сам не питонщик, но если правильно понял код, то почему print 'The computer is fine!'? Он же уже несовсем fine.
    • +1
      Попробуйте перечитать статью! else выполняется только если цикл отработал до конца.
      • 0
        Наоборот, else выполняется только если цикл до конца не отработал. Попробуйте перечитать статью! :)
        • 0
          Промахнулся, ответил ниже
  • 0
    Напрасно вы так поспешно заявляете! Всё таки прочитайте статью на досуге!
    Бьюсь об заклад, что вам неизвестно, что else можно ставить сразу после окончания цикла for! Для чего, спросите вы? Когда элементы перебираемой последовательности будут исчерпаны, управление перейдет коду, находящемуся в блоке else
    • 0
      да, я уже разобрался, спасибо!
      конечно, идея хорошая, но как-то оно неинтуитивно, мне кажется.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.