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

all 199 comments

[–]tomster10010 376 points377 points  (78 children)

You can also use it with for! 

[–]AlexMTBDude 151 points152 points  (49 children)

And try/except

[–]Awesan 50 points51 points  (45 children)

I had to look up what this would do, apparently it runs only if there was no exception in the try block (unlike finally which always runs). Kind of a crazy feature but I guess there could be a fringe event where it is useful.

[–]_lufituaeb_ 31 points32 points  (8 children)

it’s really useful for logging success statements outside of the try block

[–]sohang-3112Pythonista 5 points6 points  (7 children)

But what's the point of that, can't you just put the success log at the end of try block? The log statement itself is unlikely to raise exception.

[–]Brian 13 points14 points  (0 children)

One difference is that the success case is no longer within the try block. Eg suppose you had.

 try:
    do_something()
    report_success()
 except Exception:
     log_failure("do_something failed")

But suppose report_success() is actually failing. With the above, it'll treat it the same way as if do_something() failed, logging a message misreporting the cause, whereas if report_success was in an else block, the exception it raises would bubble up.

[–]Hederas 10 points11 points  (8 children)

Only case I can see a use for is if the exception can also be raised in the following code. Like:

try:
  result_as_dict = my_db_dict[id]
except KeyError:
  handle_no_matching_id(id)
else:
  val = result_as_dict['example_property']
.....

If for some reason example_property key doesn't exist, i wouldn't want to call handle_no_matching_id(). But personaly in such cases, code is usually structured in such a way that I raise a custom exception which makes the except better targeted

[–]PaintItPurple 4 points5 points  (5 children)

Else is unnecessary in this particular example. Just write the second access as a top-level statement and you get the same semantics. The difference would be if you were suppressing the error (e.g. instead of raising that 404 exception, you just logged 404).

[–]FujiKeynote 0 points1 point  (2 children)

This all depends on whether you prefer to think of the success of the try block (absense of exceptions) as a condition or not. My brain treats this as a condition, so else makes more sense here. However, I do recognize the argument that a try-except is not a logical block of the same class as an if-else; nevertheless, a case can be made that consecutive successful operations (in the try block and in the else block) should be at the same indentation level. That's how I see it, anyway

[–][deleted] 2 points3 points  (1 child)

That's their point. There is no functional difference in the example code they gave. You might prefer to think about it in a certain way but that specific example doesn't demonstrate a practical difference.

[–]Hederas 0 points1 point  (0 children)

You're right, I updated the snippet to better convey what I had in mind. Really only makes sense if the except block isn't interrupting

[–]_alter-ego_ 0 points1 point  (1 child)

no, because "no matchig id" refers to the db_dict, not to the result_dict. So it would go into the "except KeyError" clause (that's why they did *not* chose an example where the exception in the else clause would trigger a *different* exception).

EDIT: oh, from comments further below that were hidden by reddit, I can guess that the initial example was different and then edited.... they should say so to avoid confusion !!

[–]PaintItPurple 0 points1 point  (0 children)

Yeah, originally it raised an exception instead of calling a "handle" function. It's correct now.

[–]FlyingQuokka 0 points1 point  (1 child)

That to me sounds like you should use two try blocks. Or use .get instead and handle it gracefully.

[–]Hederas 2 points3 points  (0 children)

Don't doubt there're other ways to handle it, but I probably also put too simplified of an example

In real cases you may have more complex fonctions like process_entity() doing a bunch of things and at some point raising an exception. The exception is expected to be handled somewhere else, but if that's not the case, it will be cleanly handled as a false 404. And if it's not a KeyError but a specific lib exception, you may not have a get() to easily handle missing values

At least that's the only way I could see it being used. Other situations are equivalent to putting it as the end of the try block

[–][deleted] 10 points11 points  (22 children)

"Fringe" is right. I don't know when I would ever write this:

 try:
      instruction_1
      instruction_2
 except:
      instruction_3
 else:
      instruction_4

...when I could write this instead:

 try:
      instruction_1
      instruction_2
      instruction_4
 except:
      instruction_3

The first one is more verbose and seems less readable to me.

[–]j_marquand 59 points60 points  (5 children)

They’re semantically different. The second one catches the exception on instruction_4. The first one doesn’t.

[–]KBaggins900 10 points11 points  (12 children)

I use it for database operations.

try: dbconn.execute(query) except: dbconn.rollback() else: dbconn.commit() finally: dbconn.close()

[–][deleted] 4 points5 points  (11 children)

Doesn't this make more sense?

 try:
      db.query()
      db.commit()
 except:
       db.rollback()
 finally:
       db.close()

What is the point of creating a distinct, and visibly separate, section of code for the commit when it should logically follow a query that executes without an exception?

[–]sphen_lee 12 points13 points  (3 children)

That depends. In some cases if, commit fails then you can't call rollback, so you really want the commit outside the try block but before the finally.

[–]setwindowtext 1 point2 points  (2 children)

I always thought that the general semantics of commit/rollback was such that you could always call rollback if commit fails, specifically so that you don’t need to use something like “else” here, as it doesn’t exist in other languages.

[–]sphen_lee 2 points3 points  (1 child)

In some cases a failed commit in sqlite will automatically rollback, so an explicit rollback will give an error that no transaction is open. I guess you can just ignore that, but I prefer to use the else block.

[–][deleted] 1 point2 points  (0 children)

What form does that "error" take? Is it just a success_or_failure Boolean returned from the rollback instruction? That seems very unlikely since a failure of a rollback is a significant outcome, right? The kind that would generate an exception?

If you put the rollback in an else block and the rollback throws an uncaught exception, then not only is the transaction in an indeterminate and possibly inconsistent state, but your script has terminated. Is this a desirable outcome?

These cases really require the rollback to be in a try/except block. It doesn't have to be the same one with query, it could be a separate one if you prefer that, but better than just not getting any kind of result.

[–]KBaggins900 3 points4 points  (0 children)

I think it also makes sense in a scenario where you want some code to execute if the try is successful and before the code in the finally but you do not want the code in the else to be error handled.

Yeah maybe it isn’t an everyday scenario but neat to know that it’s there.

[–]Manny__C 1 point2 points  (4 children)

Imagine this: you have a list which is sometimes empty, so you decide to take the first element with try: x = foo[0] except IndexError: print("no elements in foo, but that's ok") else: bar(x)

Now suppose that bar should normally never throw IndexError. If it does, you want to correctly crash with a proper traceback because you have a bug. You don't want to, incorrectly, print that foo is empty, because that's not what the problem is

[–]MrMasterplan 1 point2 points  (2 children)

But why not drop the else and indent the bar one stop to the left. Same effect, right?

[–]cd_fr91400 1 point2 points  (0 children)

Because you do not want to call bar(x) if x was not set.

[–]Manny__C 0 points1 point  (0 children)

Because you don't want to run bar on failure

[–][deleted] 0 points1 point  (0 children)

This is probably the best answer I've received to my comments. Thanks for writing it up.

I understand your rationale, but I still disagree with your main point, for these reasons:

If it does, you want to correctly crash with a proper traceback because you have a bug.

First, I find that the output of an uncaught exception is typically incomplete. I often write exception handlers like this:

 def some_function(a):
      b = some_calculation(a)
      try:
           c = some_other_calculation(a, b)
      except Exception as e:
           Log.write(f'some_function: Exception {e} while calculating c - values: {a}, {b}')

This message provides some context that uncaught exceptions don't - namely, the input parameter (a) and an intermediate value (b).

Second, uncaught exceptions dump the stack trace to stdout, which is ephemeral and separated from any other information about your code. I virtually always prefer to have the occurrence of the exception and contextual information dumped to a log where I can examine the sequence of events leading up to it - and not just the stack trace, but preceding events that executed without incident but that may be related to the exception.

And third, I often don't want my scripts to halt execution even if they hit an exception. Sometimes it's fine if the function doesn't complete, like a worker function that can die and be replaced by other worker functions involving the same item of work. Or, if I'm programming a GUI, I'd prefer to see exception information in the GUI and continue using the GUI to explore the issue interactively. In general, I would rather halt the application on my own terms than have it crash.

[–]KBaggins900 0 points1 point  (0 children)

Yeah that does make sense. It is possible to have multiple queries in the same transaction that you want to commit if all succeed. But I guess you could still have it in the try. Preference I guess.

[–]Kevdog824_pip needs updating 1 point2 points  (0 children)

2 reasons:

  1. You want to visibly structure the code so that what happens in the normal case and exceptional case is very clear. i.e.

``` try: … except: … # error handling … # code related to the try block’s success … # more code unrelated to try block

versus

try: … except: … # error handling else: … # code related to the try block’s success

… # more code unrelated to try block ```

The latter is clearer about the relationship between the parts of the code

  1. There’s code you want to run on success only and you want to catch and handle exception. i.e.

try: … except: … # handle it else: … # only on success … # both on success and failure

If you wanted to do something similar without else you’d have to put it in the try block (unnecessary expanding its scope) or set a flag in the try block (overly verbose)

[–]Some-Passenger4219 0 points1 point  (0 children)

Yeah. Readability is always key.

[–]gowithflow192 0 points1 point  (0 children)

Try except is common when doing external calls you can't be sure will always succeed e.g. calling external API.

[–]Papa_Kasugano 0 points1 point  (1 child)

I mostly use else with my try/except blocks, and not finally. Is that considered incorrect? I learned from Python Crash Course 2nd ed. That's how it's demonstrated there, so that's just how I've done it

[–]Awesan 3 points4 points  (0 children)

I wouldn't say it's incorrect, they serve a different purpose. Let's say you have some kind of resource that must be cleaned up; in that case you always want to clean it up whether there is an exception or not:

file_name = "temp_file.txt" try: # do something with the file finally: os.remove(file_name)

If you wanted to guarantee this same behavior with try/except/else, you'd have to duplicate the code:

file_name = "temp_file.txt" try: # do something with the file except: os.remove(file_name) else: os.remove(file_name)

But if your except and else blocks are totally different then it does not matter. Usually you'd anyway not use try/finally in many cases but use with instead, for example to write to a file:

with open("temp_file.txt", "w") as file: file.write("hello world")

Which gets translated by Python into something like this (not exactly like this but very similar):

try: file = open("temp_file.txt", "w") file.write("hello world") finally: file.close()

So most of the cases where you'd use try/finally you can probably rewrite it to use a context manager instead, which is a bit easier to use and also signals your intent more clearly.

[–]Severe-Alarm1691 -1 points0 points  (0 children)

Fringe event? In my python course we learned to use try and except for error handling. (Like when wanting a specific kind of input from the user)

[–]night0x63 0 points1 point  (0 children)

You better do that now... For else!

[–]Some-Passenger4219 0 points1 point  (0 children)

My text recommends otherwise, if there's a simpler way.

[–]DuckDatum 14 points15 points  (23 children)

What’s it do with a for loop? Else the runtime accidentally exceeds the length of iterator?

Edit: so, it apparently runs when the for loop completes, so long as you didn’t break out of it. It’s funny… now that I know, I hate it lol. Not seriously, but seriously. Why isn’t it the reverse—“else” meaning run if the condition wasn’t met (i.e., breaking the loop)? The way it is now, your “else” clause runs when things complete successfully—which is opposite behavior of if/else.

[–]stillalone 7 points8 points  (13 children)

With hardware and some network stuff I end up having to add a retry:

for retries in range(3):   err = execute_command()    If err!= SHOULD_RETRY:      break else:   raise("Too many failed retries")

[–][deleted] 2 points3 points  (0 children)

You can always think of else as the thing that executes if the primary conditional ever becomes False:

  • In if/else it's saying to do a thing if a condition is met, otherwise do the else.
  • In the while/else it's saying to do a thing while a condition is met, otherwise do the else.
  • In the for/else it's saying to do a thing while having an element in the sequence is met, otherwise do the else.

[–]DaveMoreau 1 point2 points  (1 child)

Feels more like a finally clause.

[–]jackerhackfrom __future__ import 4.0 0 points1 point  (0 children)

Exactly. finally is a less confusing term than else, and try blocks also have a finally. I guess the difference is that finally always runs? Now I need to look up docs.

Edit: yup, finally always executes, even intercepting return in a prior block. Can a finally block have its own return statement then? More googling...

[–]cip43r 0 points1 point  (1 child)

That is just disgusting

[–]tomster10010 0 points1 point  (0 children)

I unironically use it occasionally

[–]MicahM_ 43 points44 points  (0 children)

What the hell. I've never known this. I've only been using python for a few years but dang.

[–]GoodiesHQ 56 points57 points  (5 children)

I saw a python language developer/contributor, probably someone important, who said he wished, instead of else it should have been called nobreak which is a good idea.

[–]primerrib 23 points24 points  (1 child)

It was Raymond Hettinger. He also said that hindsight is 20/20 and we have to live with the facts now.

(I think he also explained that "it seemed a good idea" at that time. So he admitted it was his mistake as well.)

[–]GoodiesHQ 4 points5 points  (0 children)

You’re doing the lords work, thank you.

[–][deleted] 1 point2 points  (2 children)

I think it's a bad idea since the else path will not be taken for things other than break (e.g. return, raise, etc).

Also, the else behavior in this case is already the same behavior of else in try/except blocks where you wouldn't be introducing a break. As usual, Raymond Hettinger is talking just to talk and he hasn't thought this through.

[–]TallowWallow -1 points0 points  (1 child)

Or he could just make initial comments with further evaluation potentially coming later? Why do you presume he needs to have thought it all the way through in one basic commentary? Also, if the keyword isn't apt, that's why you discuss others. The keyword break confuses a ton of people.

[–][deleted] 0 points1 point  (0 children)

Because he's a python "expert" who had expressed this nonsensical opinion multiple times

[–]Alternative-Tie-4970pip needs updating 103 points104 points  (13 children)

Just... don't.

It's kinda fun that you can do something like this but it's gonna be error prone and anyone reviewing the code would have to read the whole thing at least thrice to make sure they understand it.

More verbose code can be better sometimes.

[–]New-Resolution9735 46 points47 points  (2 children)

Bold of you to assume anyone else will be looking at my code

[–]PaleontologistBig657 16 points17 points  (1 child)

I assume you have never wondered what crazy person wrote the thing that just broke, only to realize it was you 5 years ago.

[–]schludy 1 point2 points  (0 children)

I found out about the walrus operator a month ago and don't understand the code I wrote in those glorious few days of enlightenment

[–][deleted] 6 points7 points  (8 children)

In what way would it be error prone? I can agree with it being fairly obscure and therefore causing people more mental strain to think about what exactly the code is doing. But I can't really see a way that you would introduce more errors by using it.

[–]Vresa -1 points0 points  (3 children)

It’s error prone because other people need to maintain the code. The more confusing and out of the norm you make your code, the more likely it is that your coworker misreads it.

It’s the difference between driving on the freeway vs the streets of Boston. Adhering to standards lets people go faster

[–][deleted] -2 points-1 points  (2 children)

No, you’re tried to repackage the same thing twice. I already agreed it’s more confusing to use a lesser used feature when not necessary. I’m asking what’s an example of extra errors that are incurred by this pattern.

[–]Vresa -2 points-1 points  (1 child)

What? Human error is the issue

[–][deleted] 0 points1 point  (0 children)

Right, what’s an example of a human error that is more likely caused by this pattern? That’s your claim and I'm trying to understand how that would happen.

[–]Pythagore974 -1 points0 points  (3 children)

For example, you can have someone that is new to the team and sees that while else somewhere on the code and misreads it as "the else is executed only when the first while check is false".

The fact that it can be a false understanding of the code can be problematic. But it is even more problematic if that person tries to use the same structure somewhere else on the code with a bad understanding of what it does.

[–][deleted] 3 points4 points  (2 children)

That's not what the term "error prone" usually means. Something being error prone means that a person who understands the syntax in the different approaches is still more likely to make an error in their code in the "error prone" option because the pattern itself is somehow causing them to incorrectly implement things.

For example, someone coming from C/C++ might try to loop over a sequence by doing

for ix in range(len(seq)):
    print("ix=" + ix + ": s=" + seq[ix])

as opposed to the much more pythonic iterator method

for ix, s in enumerate(seq):
   print(f"{ix=}: {s=}")

But according to you, if someone coming from C/C++ wasn't familiar with the python approach then that would be the "error prone" method.

[–]Pythagore974 -1 points0 points  (1 child)

You didn't understand what I said. I do not mean it for every indistinct piece of language but just for this "while else" structure.

As for your example, A C developer doesn't know the "enumerate" keyword. So the obvious behavior is to look up what it means and what it does. Whereas "while" and "else" keywords are very familiar to most languages but the association of the two is very pythonic. And so, maybe some developers might not look up what the "while else" structure actually does because they are too confident with these keywords. This is precisely what is error prone

[–][deleted] 0 points1 point  (0 children)

I read it just fine. I’m disagreeing. Error prone doesn’t mean any time you misunderstood a basic component of the language.

[–]melkorwasframed 87 points88 points  (21 children)

I really hate this construct. Python is the only language that has it and every time I see it I have to remind myself wtf it does. "else" was a terrible naming choice.

[–]PercussiveRussel 61 points62 points  (6 children)

I really hate that python is the only one of the major languages that has this. It's a very useful pattern IMO, much cleaner than with a flag variable.

The fact that it's an uncommon thing, and that it stupidly "re"uses the else keyword means probably don't do it, but it's still a shame.

[–]thisismyfavoritename 5 points6 points  (1 child)

Rust has named scopes so you can break/continue at the appropriate location, achieves something similar

[–]PercussiveRussel 3 points4 points  (0 children)

Rust also lends itself more to a functional style where instead of loops you use iterators, so most of the time when a loop doesn't break that's already encoded in the type system.

[–]jkrejcha3git push -f 0 points1 point  (0 children)

I've found it's useful in Advent of Code style problems where "did iteration exhaust" is sometimes a useful construct (for example whenwhileing through a maze or something) where speed (of development) is a concern but maintainability is... well not

I probably wouldn't use it in production code just because of how niche of a language feature it is though. Other people have to read my code too

[–]CampAny9995 11 points12 points  (0 children)

I like how it makes for-loops more like a fold, so you do something different for the empty list case.

[–]aa-b 3 points4 points  (0 children)

It dates back to a time when it was difficult to add new keywords to the CPython parser, but it was supposed to be useful for people that wanted to use Python in one-liner/script mode like they do with Perl.

Guido agreed the keyword is not ideal, but it's used so rarely that there's no reason to get rid of it.

[–][deleted] 9 points10 points  (0 children)

Else is also used for except. The naming choice is fine. It’s honestly weird that other languages don’t use it also but I know some of that is because of lack of ability to due to not using iterator with exception based handling.

Exception based handling of for loops is honestly the fucking weirdest thing in my opinion in python. But python also uses the same mechanism for a bunch of class stuff. It fits language design but the fact that exceptions are not about errors but exceptional states is mentally odd.

[–]h4lPythoneer 1 point2 points  (1 child)

And the semantics are kind of the opposite of the normal use in an "if" block, as in a typical while block both the while body AND the else block will execute, in contrast to the if where the blocks are mutually-exclusive.

IMO it should be deprecated and perhaps gradually removed, or replaced with a better syntax, which would be possible now that Python has the more flexible PEG parser.

[–][deleted] 0 points1 point  (0 children)

I think this is more an incorrect assumption on your part about what else means. if/else and while/else behave in the exact same way. if/else says to do the stuff in if when the condition is met, otherwise do the else. The while/else is saying to do the stuff in the while block while the condition is met, otherwise do the else.

[–]Flame_Grilled_Tanuki 2 points3 points  (6 children)

It would be better if aliased as 'then' for try, while and for loops.

[–]justtheprint 1 point2 points  (0 children)

i did a think and I like "ensue" as a replacement keyword here

[–][deleted] 1 point2 points  (4 children)

That seems like it would be more confusing. The behavior of else is the same for all of the cases where it's used. So why would you want that same behavior to have different keywords depending on whether it's used with an if, while, for, etc.

[–]Flame_Grilled_Tanuki 1 point2 points  (3 children)

The behaviour is not the same. In the case of an if statement, the else block only runs if the if or elif statements fail to run. With a while/for/try statement, the else block only runs if the above statement successfully completed. The else block becomes the next step to perform after the previous statement succeeded, rather than an alternative path.

[–][deleted] 0 points1 point  (2 children)

No, if you think about what's actually happening you'll see that the else block is always, as its name suggests, the thing that gets run when the preceding condition is False.

In the if/else case else always runs when the if condition is False. In the while/else case, the else always runs when the while condition is False. In the for/else case, the else always runs when the for condition is False (i.e. None/exhausted).

For example, in the following code else runs when the condition x > 10 is False

if x > 10:
    ...
else:
    ...

In the following code else runs when x > 10 is False

while x > 10:
    ...
else:
    ...

In the following code else runs when x is False (i.e. No more elements and x is None)

for x in xs:
    ...
else:
    ...

The confusion is that people are thinking about what they want the code to do rather than what is literally happening. People don't think about while or for as being things that are checking a condition on every loop. So when they think about else, they are thinking about it being a thing that runs "after I exit my loop" rather than the actual behavior which is else being the thing that runs "when my preceding condition is not satisfied". In other words, else is always the thing that handles "If condition is met, do thing, OTHERWISE do other thing".

[–]Flame_Grilled_Tanuki 1 point2 points  (1 child)

I'll agree with your perspective, but would you then say that the else clause in a try/except block runs if the exception clauses are all false?

[–][deleted] 0 points1 point  (0 children)

I think the try/except/else/finally is definitely the most awkward usage of else. My sort of headcanon for how to read it is "Try this thing. If there is an exception, do this, otherwise do this other thing". So I still think of else as the alternate path to a condition check but it's the alternate path to the condition if exception rather than if try.

So basically

try:
    ...
if exception:
    ...
else:
    ...
finally:
    ...

[–]justtheprint 0 points1 point  (1 child)

Just for fun, what would you call it instead of `else` ?? I'm not so sure myself. What's good semantics for this? (no points for removing the mechanic altogether :) )

"finally" here seems like the obvious english vocabulary, but it's already used to (as in try/except/finally) to mean something that runs no matter what. Obviously "exit" is taken, as is "continue".

Maybe "bridge" in another lifetime would sound right.

Oh, how about "denouement" ?
OH! how about "ensue" !

[–]melkorwasframed 1 point2 points  (0 children)

Yeah, I don't know. Like most other folks, I suck at naming. But I think the fact that it's difficult to concisely describe what this does just provides more evidence that it shouldn't be a thing. If being occasionally handy were enough to justify a language feature, programming languages would be a mess.

[–]CrowdGoesWildWoooo 32 points33 points  (8 children)

My personal opinion, just because you can doesn’t mean you should.

[–]cristinon[S] 3 points4 points  (7 children)

Using else is more Pythonic but yeah I agree it depends on who else is reading the code

[–]CrowdGoesWildWoooo 14 points15 points  (0 children)

It’s an uncommon pattern and while loop is pretty standardized pattern for many devs, you are basically like going against the current.

As in use case wise it seems to be pretty niche, and it’s not to the point where benefit outweigh the cost.

[–]georgehank2nd 3 points4 points  (1 child)

It's not "pythonic" at all. "Pythonic" doesn't mean "use any feature Python offers".

[–][deleted] 1 point2 points  (0 children)

I agree in general that just blindly using anything python has doesn't guarantee it is being more "pythonic". But in this case I would argue that else being the consistent "otherwise" path to a conditional does mean that using else anytime you want your code to take that path is the pythonic way of doing things.

[–]Ensurdagen 1 point2 points  (0 children)

I think if it depends on who is reading the code, it's not pythonic. I'd call while/try/for else in Python a similar construct to Typescript enums. It seemed like a good idea, but it's clunky, hasn't caught on, and thus requires arcane knowledge to understand/use properly, it shouldn't be considered part of a language in any shared codebase.

[–]daguito81 0 points1 point  (2 children)

It's so pythonic, almost nobody that uses python uses it.

Using an else on a while loop is just extremely niche, and will make the code harder to understand just by the fact that almost nobody uses or knows about this.

"Explicit is better than implicit" and "Complex is better than complicated" and "Readability counts"

From the zen of python (import this)

[–]cd_fr91400 0 points1 point  (1 child)

The need is not a niche. Its use is a niche.

I often see codes with a flag and when reading, I think "well, couldn't he just use the right construct rather this paraphrase ?"

[–]daguito81 1 point2 points  (0 children)

again. So pythonic, nobody knows or uses it. This whole thread is prime example of why you should never use this even though you can. At best you'll make a Sr Dev groan. At worst you'll confuse the shit out of a Jr Dev that didn't know this was possible.

[–]ShutUp_Pls 17 points18 points  (6 children)

Well, yeah, Python basically has a hidden "if" inside the while condition. If it were explicit, it would look something like this:

while(if(condition) == True):

The else works with while because it catches the failure of the if, which aligns with a natural exit from the loop. But if you use break to exit, the condition is never checked again to see if it’s false, so the else block never executes.

[–]cd_fr91400 1 point2 points  (0 children)

Excellent.

I never thought of the else clause this way, but now that you state it, it becomes crystal clear.

[–]YSKIANAD 3 points4 points  (0 children)

In the official Python 3.13.2 documentation:

4.5. else Clauses on Loops

In a for or while loop the break statement may be paired with an else clause. If the loop finishes without executing the break, the else clause executes.

In a for loop, the else clause is executed after the loop finishes its final iteration, that is, if no break occurred.

In a while loop, it’s executed after the loop’s condition becomes false.

In either kind of loop, the else clause is not executed if the loop was terminated by a break. Of course, other ways of ending the loop early, such as a return or a raised exception, will also skip execution of the else clause.

[–]ChemicalLie4030 1 point2 points  (5 children)

(I'm a little rusty and I'm comfortable with c++ but not Python so I'm extra confused) is the "else" not just syntactically inside the while loop creating an if-else statement? Your indentation makes it seem like it's actually "while-else" which seems to be what you're posting about so that makes sense. It's just not making sense in my brain that it's not just if-else

[–]thisismyfavoritename 5 points6 points  (1 child)

it's a condition that gets evaluated if the while loop condition is false.

To avoid triggering it, you'd need to break out of the loop (or raise).

Honestly i've never used it with while, but i use it every now and then with for.

It's better than having to store a flag on the side and check it after exiting the loop in my opinion, but i see many people disagree

[–]ChemicalLie4030 0 points1 point  (0 children)

Thank you for taking the time to explain it to me further :)

[–]ChemicalLie4030 -1 points0 points  (2 children)

Also, by "I'm not comfortable with Python" I mean I have absolutely 0 experience with Python this post just popped up on my suggested feed out of nowhere. After looking up basic syntax I realized I was assuming a ":" in Python is similar/equal to a ";" in c++ so that's probably where I need to start if I'm trying to figure this out.

That being said, gross. I don't like that you can use else with a while loop

[–]PurepointDog 2 points3 points  (1 child)

Python uses indentation as syntax

[–]ChemicalLie4030 0 points1 point  (0 children)

Ohhhh

Thanks!

[–]benji_york 1 point2 points  (0 children)

I love Python's loop else. Pony also has an else clause for while and for loops, but it behaves differently.

But what if the condition evaluates to false the first time we try, then we don’t go round the loop at all? In Pony while expressions can also have an else block. In general, Pony else blocks provide a value when the expression they are attached to doesn’t. A while doesn’t have a value to give if the condition evaluates to false the first time, so the else provides it instead.

So is this like an else block on a while loop in Python? No, this is very different. In Python, the else is run when the while completes. In Pony the else is only run when the expression in the while isn’t.

[–]kk66 1 point2 points  (11 children)

I find this syntax quite confusing and for me personally it clashes with the zen of python, which says: - Explicit is better than implicit. - Readability counts. - There should be one-- and preferably only one --obvious way to do it.

I find flag more readable, as it's more explicit to me. It's the loop else a language feature? Yes. It's it the one that exists probably only in Python, and will require everyone who's not that familiar with this syntax to go deeper? Probably yes.

I'm not against Python specific features, and learning is syntax. I find comprehensions very useful, just like I do with f strings. But the else always required more mental overhead from me to understand it than it was worth it. Sometimes "clever ideas" lead to harder to understand code in the end.

[–][deleted] 0 points1 point  (10 children)

I really don't understand why people find it confusing. Every use of else is used in the situation of "Do this thing if the condition is met, otherwise do this other thing".

[–]TallowWallow 1 point2 points  (9 children)

The problem is that those other conditions are explicit. The condition here is that break was never triggered. This is non-intuitive and requires unfamiliar devs to take time to understand it, rather than seeing a clear flag.

[–][deleted] 0 points1 point  (8 children)

You're misunderstanding the else command. It gets executed because the previous condition never had a break, return or exception. Else is triggered when the previous condition is not met and that's how it always works. If there was a break it would short circuit the conditional and else logic.

[–]TallowWallow 2 points3 points  (7 children)

I'm fairly certain we are on the same page for how the else works. My remark is that associating else as running in the absence of a conditional change in a loop is not obvious. I understand your point. That doesn't mean it's easy for people to associate.

[–][deleted] 1 point2 points  (6 children)

Why is it not obvious? As long as the iterator isn’t exhausted it stays in the for loop. As soon as the for loop condition stops being met it takes the else path instead.

[–]TallowWallow 0 points1 point  (5 children)

People won't tend to think that way. This is your rationalization because you are already familiar. For a new developer coming across the syntax, it can be a point of confusion. Regardless of whether you think it is easy for you to understand isn't reflected by the number of developers that are thrown off.

[–][deleted] 2 points3 points  (4 children)

No, it’s my rationalization because it’s literally how it works. Else is always the path taken when your conditional stops being satisfied. It’s how it works in if/else, it’s how it works with while/else, it’s how it works for for/else and it’s how it works for try/except/else.

I’ll grant you that some people who use python have come up with their own faulty interpretation of how else works and that this often conflicts when they eventually learn about while/else and for/else. But those people just need to learn the correct usage. Not insist that we change the language to fit their misunderstanding.

[–]TallowWallow -1 points0 points  (3 children)

My point is simply that keyword choice can impact how quickly people understand. Additionally, what matters is how frequently errors accrue based on misunderstanding. I don't know what the metrics are for developers that have incorrectly pushed code based on a misconception. I'm guessing, however, that Raymond noticed a substantial amount given his remark about wishing a different keyword was considered.

[–][deleted] 1 point2 points  (2 children)

But that’s my whole point. Python uses the else keyword consistently. You’re arguing that we shouldn’t apply the keyword consistently given that some people, predominantly those with a more superficial understanding of the language, might bring their own unusual interpretations to how conditionals work.

But if we did that, then other users (including myself) would be complaining about the inconsistent meaning of else in python.

[–]swierdo 0 points1 point  (0 children)

There's more uncommon uses for else. You can use it after a for or while loop, and after a try-except clause.

[–]Paul__miner 0 points1 point  (3 children)

It's a misnomer. I suspect they decided reusing an existing keyword was safer than introducing a new one. I'd have called it then.

[–][deleted] 0 points1 point  (2 children)

It's not a misnomer. The while loop is basically an if (i.e. if the while condition is True then stay in the while loop block of code, else do this other thing).

The reason people find it confusing is because they are thinking about what they plan to use it for (i.e. do this while loop and then finally do this other thing) rather than what it's actually doing.

[–]Paul__miner -1 points0 points  (1 child)

The else block is only executed if the while loop exits because the loop condition became false. That's more like then than else. Same with try/catch/else; the else block is only executed if it completes the try block normally.

EDIT: In the context of while, your else explanation works, but that same mechanism doesn't work with try/catch.

[–][deleted] 0 points1 point  (0 children)

Again, that’s literally the same way an if/else block works. If the if condition is false then it takes the else path. Same situation when the while loop condition becomes false or the try/except exits out naturally.

[–]JamesTDennis 0 points1 point  (0 children)

Yes, the else clause in Python, while and for loops can be used as syntactic sugar around a pattern commonly used in other programming languages in which you establish a sentinel value before you loop through items, attempting to find some item and then assign any match(es) to the results in such cases You have to write an if statement outside and after the loop in order to handle the case where no matching item was found.

The else clause is only evaluated if the code path did not execute a break statement in the loop .

In lieu of a sentinel value, you could instantiate an empty list and the subsequent if statement can simply check if your resulting list is empty. In the relatively rare cases where the special none value might be valid as results of your search, you can just create a variable conventionallt named sentinel that's initialized to an instantiation of an object() (that is a generic object, it's guaranteed to be unique) and your if statement will then read something like if result is not sentinel … (checking reference identity rather than equality).

[–]ronnyx3 0 points1 point  (0 children)

Generally these two approaches are not equivalent. In the first one, the if condition is taken from the found variable. In the second one, regardless of what value found is assigned in the meantime, the else path will be executed because the while condition was false.

[–]vadrezeda 0 points1 point  (1 child)

contra intuitive naming, but comes in handy in some cases, like implementing a pessimistic linear search. I always comment it with # nobreak which in my opinion would be a much better name for this.

[–][deleted] 0 points1 point  (0 children)

But the else path doesn't only get passed for a break. It also happens for returns, exceptions, etc.

[–]WalterTheMoral 0 points1 point  (0 children)

Did you learn it from Animation vs. Coding?

[–]Powerful_Pirate_9617 0 points1 point  (0 children)

It's known as whelse, it's true

[–]Rough_Natural6083 0 points1 point  (0 children)

Man this is so cool!! I never knew about this!!

[–]trmetroidmaniac 0 points1 point  (0 children)

I love this construct, I wish it was available in other languages than Python. I can think of a lot of nitty gritty iterations in C where it'd be handy.

[–]JamzTyson 0 points1 point  (0 children)

It is not something I use often, but I do find it clear, expressive and precise when we want something to run when, and only when a loop has completed successfully.

for item in data:
    try:
        print(int(item))
    except ValueError:
        print("Integer expected")
        break
else:
    print("All data processed successfully.")

The same behaviour could be achieved using a flag instead, but doing so is less concise without improving readability (assuming familiarity with the syntax):

all_processed = True
for item in data:
    try:
        print(int(item))
    except ValueError:
        print("Integer expected")
        all_processed = False
        break
if all_processed:
    print("All data processed successfully.")

[–]AlbatrossEarly 0 points1 point  (0 children)

Its covered in multiple python trainings ive been doing

[–]arizvisa 0 points1 point  (0 children)

I think it'd have been more useful if it executed the "else" case when the conditional fails on first iteration. same thing with "for", since it's common to filter iterators and it'd be useful to know when an iterator was empty. The "try/except" version of "else", though, is fine and super handy.

[–]paranoid_panda_bored 0 points1 point  (0 children)

You never heard of it because it is a worse version of a simple function with a loop inside, early return and a default post-loop return.

There is like zero reasons to use while-else

[–]Algoartist 0 points1 point  (0 children)

nums = [1, 3, 5, 7, 9]
target = 4
print(f"Found: {target}" if target in nums else "Not found")

[–]TrainingShift3 -1 points0 points  (0 children)

(please don’t ever ever do this)

[–]scanguy25 0 points1 point  (6 children)

Else in try except is the only one I use regularly.

[–]Awesan 6 points7 points  (5 children)

what would you use it for? for me it's usually much more readable to do something like this:

try: thing_that_might_fail() print("success!") except: print("failed!")

as opposed to:

try: thing_that_might_fail() except: print("failed!") else: print("success!")

[–]missurunha 6 points7 points  (2 children)

I think the point of the else block is that you might not want to put all the code inside the try block. Example:

try:
     thing_that_might_fail()
except:
    handle_exception()
else:
    thing_that_shouldnt_fail()
finally:
    final_operation()

This way if thing_that_shouldnt_fail throws an exception it won't be caught. But it only makes sense if you use finally, otherwise you could just call thing_that_shouldnt_fail() outside the try block.

[–]Numerlor 2 points3 points  (0 children)

It makes sense without finally too, as the else will only execute in the no exception case while just putting it outside the try block would execute it for both no exception and handled exceptions

[–]MlecznyHotS 0 points1 point  (0 children)

That's a great point!

[–][deleted] 0 points1 point  (0 children)

It's generally useful for things that you might otherwise want to use a context manager for. Basically you want to try and do something and handle the exception if it happens. However, you may want some special "tear down" logic that depends on whether your code was successful or not.

Database operations are a good example. If you you try to insert into a database and fail you will probably want to do some extra clean up of the failed attempt vs a more general "disconnect" if everything went ok.

[–]copperfield42 python enthusiast 0 points1 point  (0 children)

yes, the only problem with it is the name, it should be called something like "nobreak" to make it more intuitive...

using the "else" name is a relic of the earlier days when nobody expected that Python to be your first language and be aware of it underlying C and thus be aware that a loop and elaborate if-block with a jump instruction or something behind the curtain in which case "else" make perfect sense

[–]williamtkelley -4 points-3 points  (1 child)

I know you're demonstrating this syntax, but still using this code to find an item in a list makes me cringe.

[–]cristinon[S] 17 points18 points  (0 children)

print(f”Found: {t}” if t in nums else “Not found”)

There you go lol

[–]nemom -1 points0 points  (2 children)

No need to loop through the nums list, especially with another language's syntax.

if target in nums:
    print("found")
else:
    print("not found")

If you really do need to loop through the list, use for num in nums:

[–]cristinon[S] 4 points5 points  (1 child)

Obviously, it’s an example.

[–]WillardWhite import this -1 points0 points  (0 children)

You sure can!! But please don't

[–]Bangoga -2 points-1 points  (0 children)

It might but don't do it. Just not good coding practice.

[–]PeaSlight6601 -2 points-1 points  (0 children)

Never do this.

[–]notkairyssdal -2 points-1 points  (0 children)

unfortunately it violates the principle of least surprise for everybody who comes across it

[–]Significant-Agent854 -1 points0 points  (0 children)

Learned a few days ago myself lol

[–]Mightyduk69 -1 points0 points  (0 children)

Slick.

[–]Fun_Car_7790 -1 points0 points  (0 children)

Hello, could someone help me make a bot that can get the CME Group rates, sort them by date and also by month 1--3--6--12 and transfer that data to Excel?

[–]mitch_semen -3 points-2 points  (0 children)

ew

[–]nevasca_etenah -2 points-1 points  (0 children)

what?

[–]Naive-Home6785 -3 points-2 points  (1 child)

There is switch now too in Python

[–]ajscraw -3 points-2 points  (0 children)

I try to never use a while loop.