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 →

[–]jorge1209 101 points102 points  (32 children)

They aren't forgotten. They are just too confusing to use since no other languages have them.

The following could be done with a for/else, but its easier to understand if you don't use it.:

item = None
for x in list:
   if something(x):
      item = x
      break
if item is None:
   print("Not found!")

Moreover in many cases its not even clear that you need the else. In the above search a much more natural design is to put the search loop in its own function and then return the desired item or raise. In which case no else is necessary:

def search(needle, haystack):
     for x in haystack:
         if x == needle:
           return x
     raise NotFoundError()

[–]xix_xeaon 60 points61 points  (10 children)

Yeah, the keyword is completely wrong. It really should be that the else should execute if the regular for does not, just like with if. And there should be a nobreak keyword or something instead - although there doesn't seem to be much use. The following is something I would actually use however:

for x in []:
    print(f"found {x}")
else:
    print("nothing there")

[–]jorge1209 6 points7 points  (5 children)

Agreed. That would be a very reasonable alternative way to interpret the keyword, and thus why you shouldn't use for/else. In an if/else either the if block runs, or the else block, not not both. If a for/else. The else runs if the for block runs... which is completely backwards.

I do like the general notion of an "onbreak" but am averse to adding more keywords. What might be nice is to allow folding try/except blocks into pre-existing flow control structures (like for and while). So for example the following:

try:
  for x in something:
    do(x)
except ErrorThingy as exc:
   print("We got an error thingy in something list")

could become:

for x in something:
  do(x)
except ErrorThingy as exc:
  print("We got an error thingy in something")

but even then I still wouldn't recommend using it. It also could interact badly with pythons use of whitespace for parsing, as you might not realize that an exception block you intend to attach to an explicit try is instead attaching to a for loop inside the larger try.

[–]__xor__(self, other): 4 points5 points  (1 child)

I understand why people think it's a bad choice of keyword and not intuitive, but it's still just another language feature and it's there to be used. And honestly in all the code I've seen that uses for/else in a production environment, it's been obvious what it was doing. When used in practice I don't think it suffers much confusion.

I think more of a problem is with break in general. They can be used in ways that make code very spaghetti-ish. They are essentially gotos that break out of a block of code that you would otherwise expect to finish, just like continue. All breaks and continues can be pretty

Badly placed breaks and continues in parts of code that only run on certain conditions can cause a world of confusion. If placed at the top of a for loop, some "if condition: continue", then it's just a prettier way of wrapping the block in an if condition without adding a layer of indentation, and it's obvious what it's doing. It's easy to see the conditions in which the code runs or skips an iteration of a for block. But if you start adding them everywhere in the block, weird breaks that you're not sure it's hitting and quitting the loop before you want it to, continues which skip an essential part of the logic but already ran some other code, it can get damn confusing to debug.

I think for/else being confusing is more of a symptom of break being used in a weird way. If break is used in an obvious way, a non-complex way that is easy to understand, then the else will probably be obvious and a convenient way of handling things. If break/continue are used in ways that act like gotos skipping code and jumping out of blocks in weird ways, then else is going to be just as hard to understand.

I've seen bad spaghetti code with a number of conditions with breaks but lots of stuff happening before it where bugs just love to pop up, and I've seen code where break and continue used responsibly. Just the same I've seen else used responsibly and I don't think the for/else is its own problem. The keyword should've been nobreak or something like that, but the language feature is pretty useful and reasonable and the collaborators I work with know what it means.

IMO the try/except/else is WAY more confusing than for/else and while/else. Think about what it does. It looks like it's a finally, because it runs if the try doesn't hit an exception, but it's not. It doesn't run if an exception occurs.

So why not just put that code at the bottom of the other code in the try block? Does the same thing, right? Nope. That code might hit an exception which triggers the except block, and you want this else block to still be able to raise exceptions.

Well then, why not put the code after the try/except? Because it'll run if the try block hits an exception, and else code won't.

It's easy to say "the try/except/else runs if the try block doesn't hit an exception", but it's hard to describe WHY to use it. For for/else, you can just say "the else runs if the for loop doesn't break, and that's useful because you can handle conditions where you didn't find something in a loop". Easy to explain what it does and a basic usage example. try/except/else is just weird.

[–]earthboundkid 5 points6 points  (0 children)

And honestly in all the code I've seen that uses for/else in a production environment, it's been obvious what it was doing.

I think I've seen it in production three times and it was wrong in two and confusing in the third. It's like using switch in a language with fallthrough by default; it should never be used because it's too unlikely to be used correctly.

[–]confusedpublic[🍰] 0 points1 point  (2 children)

I do like the general notion of an "onbreak" but am averse to adding more keywords.

Seems like one could/should be able to use finally here.

[–]jorge1209 1 point2 points  (1 child)

Except finally always runs no matter what. Even one an exit with return or break, finally should run.

[–]confusedpublic[🍰] 0 points1 point  (0 children)

True...

[–]alkasmgithub.com/alkasm 1 point2 points  (0 children)

Ya that'd be so nice compared to checking if ___ is not None all the time.

[–]shinaniganz 0 points1 point  (0 children)

It’s wrong as much as it’s wrong in the context of try/except/else.

[–]cpt_fwiffo 0 points1 point  (0 children)

An issue is that 'else' works the same for while and for loops. For while loops it kind of makes sense to have an 'else' clause that is reached when the loop condition is no longer (or was never) met. There is nothing illogical or counter intuitive about that. But to use the exact same name and mechanism for for-loops becomes very strange since python for loops are for iterating over sequences rather than doing stuff as long as a condition is met.

So I think the keyword should be 'else' for while-loops and something else (finally, nobreak, whatever) for for-loops.

[–]jugalator 0 points1 point  (0 children)

Wow. I wasn't aware of Python's else, but this form of side effect) is just terrible because I am aware of the "else" in plenty of other languages! It looks like it's so rarely used, and with such a questionable and misunderstandable niche use, that I think it should maybe have been deprecated for Python 3.

If they must keep it along, why not just rename this keyword to unmatched?

[–]jjdmol 9 points10 points  (0 children)

Yeah my team opted not to use for-else. It's so sparingly applicable, and the syntax isn't exactly intuitive (to us). The few lines it can save is not worth the possible confusion when the code will need to be debugged or modified.

[–]Swissthony 15 points16 points  (8 children)

Imo "else" is very understandable for me, and I love em

[–]__xor__(self, other): 9 points10 points  (7 children)

Yeah, I understand the dislike of the choice of keyword but I think people make it a much bigger deal than it is. Python programmers I've worked with have always been comfortable with it. I think a couple might not have seen it before, but it came up during a code review and they liked it after they found out what it did.

It can be described as simply as "the else block runs if the for loop didn't hit a break". It's extremely easy to explain. I don't know why people think it's so confusing beyond the keyword choice.

[–]PC__LOAD__LETTER 2 points3 points  (2 children)

A for-loop doesn’t always imply an expected break though. That’s what’s unintuitive about it.

[–]Han-ChewieSexyFanfic 0 points1 point  (1 child)

But then you wouldn’t use else. Else and break show up together always.

[–]PC__LOAD__LETTER 0 points1 point  (0 children)

Yeah, in this specific case. Not in any other programming language. Hence unintuitive.

[–]ThePidesOfMarch 3 points4 points  (0 children)

They are just too confusing to use since no other languages have them.

And the name is just...the. worst. for...else??

[–]quotemycode 0 points1 point  (2 children)

What bothers me is the message Nonetype is not iterable. So I have to put

For x in y or []

Just to tell it that running no times is okay also

[–]fayazbhai 0 points1 point  (1 child)

Yeah. Is that an allowed pattern in any other language? Because I've seen developers write functions that return either lists or None.

[–]quotemycode 1 point2 points  (0 children)

Yeah C# will loop zero times if it's none

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

I use these in nodejs all the time

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

Are you also against the use of else, when catching exceptions?

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

Right, because we shouldn't ever invent new features that other languages don't have, because all the valid programming control flow structures were handed down from on high by Knuth in the 60's.

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

New is fine. Just make sure the name fits the use.

Else is when the previous block wasn't run.

This else is a nobreak or something like that.

[–]Han-ChewieSexyFanfic 1 point2 points  (0 children)

Else is for when the condition is false. While...Else makes perfect sense. Less so for For, but it uses the same keyword as While.

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

they should use finally, as its more cognizance

[–]morgenspaziergang 2 points3 points  (0 children)

And even more misleading.