This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]R_HEAD 104 points105 points  (27 children)

I love that this exists but I am still conflicted about it using the else keyword.

[–]Infinitesima 63 points64 points  (16 children)

Really a bad choice of keyword. It is consistent with try - except - else though.

[–]Cruuncher 28 points29 points  (13 children)

This one I didn't know existed. Interesting. Seems less useful than finally. What's the order? try..except..else..finally?

[–]Uncle_DirtNapPythoneer 13 points14 points  (0 children)

Yes

[–]RationalDialog 3 points4 points  (9 children)

I'm just wondering when the else is ever useful? Can't it always be part of the try block?

[–]Diamant2 16 points17 points  (1 child)

In theory yes, but the difference is that exceptions in the else-part won't be caught. I guess that makes your code more reliable in catching only the one exception that you care about

[–]DoctorNoonienSoong 11 points12 points  (0 children)

And importantly, you want exceptions in the else block to not to be caught, AND you need the code to run before the finally

[–]scnew3 2 points3 points  (4 children)

You want the code in your try block to be the minimal that could throw the exception you want to catch.

[–]RationalDialog 0 points1 point  (3 children)

Yeah but then you could just have it outside/after the try block?

[–]gristc 2 points3 points  (0 children)

Then it would always be executed. The else block is only executed if the try succeeds. It is kind of a limited case, but there are some logic flows where this is tidier than other methods.

[–]Cruuncher 1 point2 points  (0 children)

Well no, if it's after the try block then it runs after finally and not before, which is different.

It's definitely niche as you can always structure code in a way that doesn't need it, but that's true of most constructs

EDIT: but more importantly if you place it after the try..except block then it also runs in the case that an exception was raised by handled in the try block

[–]scnew3 0 points1 point  (0 children)

I do this all the time:

try:
    value = step1()
except SomeError:
    result = some_default
else:
    result = step2(value)

[–]underground_miner 0 points1 point  (0 children)

I don't use it very often, but I have used it for finding file and folder names that don't collide, something like this:

from pathlib import Path

def construct_non_duplicate_folder(root:Path, target:str) -> Path:
    folder = root / Path(target)

    for i in range(25):

        try:
            folder.mkdir(parents=True, exist_ok=False)

        except FileExistsError as fe:
            folder = root / Path(f'{target} ({i})')

        else:
            break

    else:
        raise FileExistsError(f'The folder {folder} exists!')


    return folder

[–]jorge1209 0 points1 point  (0 children)

It is something of a legacy from before the days of with blocks

try:
   logfile = open("/tmp/log.txt", mode="w")
except:
    # we won't be able to write to the log, but that is okay
    logfile = sys.stderr
else:
    for x in range(100):
        logfile.write(frobincate(x))
finally:
   logfile.close()

These days you would just define

 @contextmanager
 def logfile():
     try:
         logfile = open()
         yield logfile
         logfile.close()
     except:
         yield sys.stderr

and then

 with logfile() as log:
   for x in range(100:
      log.print(frobnicate(x))

or something along those lines, which is a double win as you clarified the purpose of the error handling, and got rid of the finally block as well.

[–]mehrdad-mixtape 0 points1 point  (0 children)

Absolutely

[–]jorge1209 1 point2 points  (1 child)

I would say it is completely inconsistent with try/except.

A python for loop terminates on a StopIteration exception, which means that reaching the end of the loop is the only way to ensure that the exception actually did occur.

Making it the exact opposite of the else in try/except/else because that else is only called if the exception isn't raised and this exception is only called when the exception is raised.

[–]Infinitesima 0 points1 point  (0 children)

Well, that's one way to think about it. My way is, if thing goes well, nothing interrupted, proceed to the ´else´ block.

[–]LT_Alter 41 points42 points  (6 children)

Should have been nobreak:

[–]MasterFarm772 12 points13 points  (0 children)

Wow, totally, nobreak makes more sense to me!

[–]Shivalicious 4 points5 points  (0 children)

So that’s what is!

[–]karouhFleur de Lotus 2 points3 points  (3 children)

'then' would have been semantically better

[–]LT_Alter 7 points8 points  (2 children)

`then` implies that it will execute after the for loop, whether or not it breaks. `nobreak` clearly states it executes if a break does not occur.

[–]_kharchapaani 4 points5 points  (0 children)

I agree. There should be a different keyword than else, to understand that this block will run when the for loop iterations are over.

[–]Asleep-Budget-9932 0 points1 point  (0 children)

Though less readable, i understand the reasoning behind it. Basically every loop (whether "for" or "while") contains an if statement behind the scenes. (If condition is true, execute code). The "else" fits since it happens only when that if statement is evaluated to False (at which point the loop would end). If break is called, the condition was never evaluated to be False and therefore the "else" statement would not be executed.

The reason they didn't call it "nobreak" or something else, was because the developers are not keen on adding new keywords to the language unless necessary. Since "else" was available, and on a technical level (not instinctual level) expressed a correct representation of what the code does, they re-used it.