5 ms·
Oh, that didn't do what I expected at all. I was expecting this to be similar to: if list: for x in list: # Some code else: # else claus
by contravariant 8y ago
Oh, that didn't do what I expected at all.
I was expecting this to be similar to:
if list: for x in list:
# Some code
else:
# else clause
which would have been quite useful to have for general iterators. But apparently it's a statement that triggers unless you use 'break' to end the loop, which just seems like a really weird use case, and smells like a goto.
- LolWolf 8y agoThis is usually quite useful, actually! It happens a lot in numerical computing where you try a bunch of possible solutions and if none are found then you output some sort of "infeasibility" result, otherwise you output the result you broke at (skipping the else statement).
- contravariant 8y agoPutting the loop in its own function would solve the problem as well (allowing you to just return the output), or if that's not an option then you need to store the output anyway so you might as well just add a check 'if output is None:'.
- jackewiehose 8y ago> which just seems like a really weird use case I think quite the opposite: What you expected seems unnecessary to me because that is already easy to do with that if-statement in your example. On the other hand, to accomplish this "for-else" you would have to do something like this: found = false for x in list: if ...: found = true if not found: ... While writing this example I noticed that I also could make good use of the negativ: Something like for x in list: ... then: ...
- contravariant 8y agoTo achieve the for else I suggested for general generators requires a similar pattern to your suggestion so both of them save equal amounts of complexity. However I'm struggling to find a use case for the python for-else definition where you don't end up doing something like 'result=something; break' anyway. What would be the point of breaking if you don't have any results? And if you have such a situation, wouldn't an exception make more sense? Conversely to check if an iterator is non-empty you need to set a boolean to true, and you can't easily skip this part so you end up setting it to true every loop. It would have been quite nice to have an 'else' condition that triggers when the generator is empty (which also fits quite nicely with all empty containers being considered falsy).
- kd5bjo 8y ago> However I'm struggling to find a use case for the python for-else definition where you don't end up doing something like 'result=something; break' anyway. What would be the point of breaking if you don't have any results? And if you have such a situation, wouldn't an exception make more sense? This is also useful for an accumulator and sentinel case. Say you're parsing a file and need to grab a null-terminated string: result = '' for b in file_bytes_iterator: if b == '\0': break result += b else: throw ValueError("String is unterminated") result will contain the parsed string, and file_bytes_iterator will be ready to return the first byte after the terminator, to continue parsing the file
- contravariant 8y agoFor what it's worth you could also write that as: result = '' for b in file_bytes_iterator: if b == '\0': break result += b if b != '\0': throw ValueError("String is unterminated") And while we're one the subject, I would also really like to have a construction like: result = '' while b != '\0' for b in file_bytes_iterator: result += b if b != '\0': throw ValueError("String is unterminated") but I fear opinion might be divided on that one.
- kd5bjo 8y agoIt's a syntax sugar feature, so its presence or absence will make little difference in the grand scheme of things -- there will always be a relatively easy way to rewrite code without it. Personally, I would prefer the loop iteration variable to only be in scope within the loop body, and generally write code as if that were true. Also, repeating the sentinel test is asking for a future edit to change one and miss the other.
- fyi1183 8y agoYour suggested replacement duplicates the break condition, which is not ideal for maintainability. Also, it only happens to work due to Python's variable scoping rules. I have wished for a for-else construct in C and C++ many times: having to broaden the scope of a loop iteration variable just to duplicate a loop break check really sucks.
- deleted 8y ago[deleted]
- xiaq 8y agoIt is useful when the "normal" flow is to terminate the loop early with a break. For instance, in a linear search algorithm: for x in li: if x == to_find: print("found") break else: print("not found") Logically, the "else" matches with the "if" introducing the "break" and means "if none of elements cause the break branch to be taken".
- deleted 8y ago[deleted]
- sooheon 8y agoIt's a special case that matches `else` with `if` at the wrong level of indentation. Seems a lot like faux-simplicity, like a lot of Python: it seems simple and "pythonic" superficially, but really forces the coder to learn special cases.
- xiaq 8y agoThe way I explained how "else" pairs with "if" in preceding inner block is more of a mnemonic device than anything; "logically" was probably the wrong word. Python is probably not as simple as some like to claim, but this is not really any kind of special case. To have a special case, you need to have a normal-case behavior that is then broken: for instance, a function f(n) that "computes the factorial of n, unless if n is 1 in which case it returns 42" contains a special case, because n = 1 used to have a normal-case behavior of f(1) = 1. Special cases require the user to learn of its existence, or they will be bitten by it. In this case, there isn't any special-casing: the support for "else" after a "for" block doesn't have any predetermined "normal-case" behavior. In most other languages, such a construct is simply not supported. Python adds a new behavior here without breaking any existing behavior.
- sooheon 8y agoHere the "normal case" I was referring to was how "else" usually appears at the same level as a predicate block.
- 8y ago
- shawnz 8y ago> and smells like a goto. What about while/break, for/break, try/except, etc doesn't "smell like a goto"? Isn't that the whole point of these sophisticated constructs, to serve as more rigorous replacements for "goto"?
- contravariant 8y agoSure, and break/continue etc. should be discouraged for that precise reason. I don't quite see try/except/finally as a goto, since I'm not sure how to make one that behaves that way. However IMO the best 'rigorous' model of programs is maintaining pre and post conditions. Escaping early when the post-condition is satisfied is compatible with this model (like with e.g. return/break/continue). Escaping in two different ways is not. If you really need to handle exceptional cases where you need to escape a loop differently then exceptions are the better option IMHO, these also have a fairly rigorous definition in terms of Monads. Obviously if actual Monads (which in the case of python would be containers / generators) are an option they should be preferred.
- shawnz 8y ago> I don't quite see try/except/finally as a goto, since I'm not sure how to make one that behaves that way. I suggested try/except because goto is very commonly used to provide similar behaviour in C where there are no exceptions. > break/continue etc. should be discouraged for that precise reason ... the best 'rigorous' model of programs is maintaining pre and post conditions ... Obviously if actual Monads are an option they should be preferred. But why? Do you have any evidence that this leads to better/more robust/easier to understand code in practice?
- contravariant 8y agoDoes personal experience count? (note also that I indicated that ultimately all of this is just my opinion). Personally I find I mostly end up creating bugs when I don't have a clear enough image of what the proper pre-condition to a loop should be and what its post-condition should be. I therefore believe that code that by its very structure can't easily be assigned pre/post conditions would make things quite difficult. Post-conditions also tend to make it easier to reason that the code is correct (provided they are documented). As for the Monad quote, well it's use is somewhat limited in Python, however returning a 'trivial' result rather than an exception usually ends up being easier to work with. Which is appropriate varies though, and in Python there's not much reason to avoid exceptions for performance reasons anyway.
- userbinator 8y agowhich just seems like a really weird use case, and smells like a goto. That's exactly what it's for. In C, this is a pretty common pattern: for( ... ) if( ... ) goto found; /* else not found */ ... found: ... I remember when I had to use a bit of Python and lamented its lack of goto, then came across this "else loop" and thought "yes, they even thought of that."
- deleted 8y ago[deleted]