Python’s For - Else
book.pythontips.com
book.pythontips.com
for i in l:
print(i)
then:
print("iteration finished without breaking")Using the much clearer C syntax as pseudo-code, suppose we have this:
for (;;) {
if (loop_guard()) {
body;
} else {
alternative;
break;
}
}
We can imagine an extension to while which lets us rewrite it like this: while (loop_guard()) {
body;
} else {
alternative;
}
Based on this framing, there is justification in calling it else.The runs counter to an intuition which would like it to be this:
if (loop_guard()) {
while (loop_guard()) {
body;
}
} else {
alternative;
}
I.e. not an alternative in the face of the failed loop guard on every iteration, but just the first loop guard test: an alternative to the loop when no iterations take place.It should also be noted that there is a `while-else` compound statement as well, wherein using a `break` immediately exits the loop, without executing the `else` clause [1].
[1] https://docs.python.org/3/reference/compound_stmts.html#the-...
> When the condition is false, it breaks.
And then you said:
> If the for loop exits normally, the condition was false.
I can understand how it can be confusing, but the inverse would not make any sense.
Another way to look at a for loop is a fancy while loop that sets a variable by some function. Every loop iteration that function is ran. If the function fails, the loop exits.
This is infact how they work. https://anandology.com/python-practice-book/iterators.html#t...
If next() fails, that is when the loop exits.
while(condition()):
...
else:
...
But that makes even less sense since `while` is semantically like an `if` with continuation. GP is right, it’s rarely used, because the syntax clashes with the natural reading of the code.For example, from [0], it’s described as:
> “The else clause is only executed when your while condition becomes false. If you break out of the loop, or if an exception is raised, it won't be executed.”
So it is very analogous to an if / else statement: the “else” after a for or while loop directly means the conditional of the loop evaluated to False.
If the program never reaches a state in which the loop conditional evaluates to False, then the else block is not entered.
Just like: only when the conditional of an if statement evaluates to False will the else block be entered.
After I understood this, I started to think that break is really the more problematic thing, because it means more like a goto sort of jump, abandoning the status of the variables governing the loop condition.
But again, I don’t like these loop-else features. They definitely violate least surprise for most people reading the code.
[0]: < https://stackoverflow.com/questions/3295938/else-clause-on-p... >
...how is that supposed to apply to "for i in l"? What is the conditional?
I don't think of python for statements as involving conditionals at all; you seem to be describing a while/else rather than a for/else.
Not saying is absolves the for/while else syntax, but it’s still using the expected iteration constructs.
So StopIteration can be thought of as an equivalent idea to “emptiness” of an iterator, either an empty container, an exhausted generator, etc.
And since emptiness is most often handled as falsiness in a bool context, it’s in some sense the natural notion of a “false” iteration condition.
This is all incredible mental gymnastics though. But (speaking as someone who loves Python) there’s unfortunately a lot of that in Python.
Presumably there is a conditional in the loop body that decides whether or not to break:
for i in l:
if ...:
break
Think of the else clause as being what to do if nothing takes the if in the body.Same for while...else. You'd only be using an else on a while if there was something in the while that could break, so it is whether or not the conditional that controls that decision happens or not that determines whether the else path is taken.
for i in []:
if True: break
else: print("Foo")
while this will not: for i in [1]
if True: break
else: print("Bar")
The "conditional" in a for loop is that the iterator (l in your example) raises a StopIteration exception. I don't really like calling it a conditional though, because it's actually handled as an exception. I guess you could think of the condition being "are there any items remaining in l? If 'no' then evaluate the else clause." I've always considered the else clause on a for loop's proper purpose being to catch erroneous cases, for instance if you try and loop over files in an empty directory. Or if you have no matches searching line by line over a file. Etc.A lazy way to think about it is that way, but as long as you remember that it's what occurs when there is no break it's fine.
try:
for i in range(5):
raise Exception
else:
print("Foo") # still won't be evaluated
except:
print("Loop aborted")
The vacuous case (iterating over []) is also kinda important if you're iterating over something of variable length in case you need to distinguish between "loop terminated normally" and "input was empty". "else" can't really tell these two cases apart.Else in a for loop is kinda obscure anyway, so its importance is marginal. It is really handy for a couple specific patterns, but I've only ever found myself using it a handful of times (and I write a lot of Python).
Now, clearly there is a check to see if it's still producing values. But it's intentionally hidden from a surface-level reading of the code.
Naming a control structure `else` when it intentionally does not have a clear conditional tied to it is odd to say the least.
But it's worse than that- the else clause is supposed to be the normal flow of the program. Programming introductions (fresh in the minds of many who use Python as it's targeted as user-friendly) often stress the fact that in an if/else statement only one or the other block is executed. But this flips that idea upside-down.
In the early stages of Perl 6 design I requested a for/else feature where the "else" block fired if and only if the for loop had never been entered...the opposite of how this works. Being literally the opposite of how I would write it qualifies as surprising to me.
(I don't think for/else is the best option, gotos are. It's just not a harmful feature, the way other aspects of Python are.)
--------
Suggestion 1 (horrific suggestion):
Call this keyword finally. This implies that no matter what the finally part will execute. This is like defining never as a synonym for true, so that if(never) always executes. Why would anyone ever do that!! Hardly any choice in a design is as bad as explicitly saying something different from what will happen. So this is a terrible suggestion.
--------
Suggestion 2 (good suggestion):
Call this keyword ifthru. (Thru being an abbreviation for through). As it says, this one will run if the loop completed. Another way to read it is if execution continued "through" the end.
So your example might read:
for i in l:
print(i)
ifthru:
print("iteration finished without breaking or exceptions")
This is extremely explicit about the condition. I picked it to be short while remaining explicit. The elif keyword is an existing precedent for mispelling for the sake of brevity.It also alerts the reader that logically there is a condition here. After all, semantically every block could start with the "then" word in English. Semantically, when you break you could also be considered to be "through" however in this case the ifthru keyword wouldn't serve any purpose at all, since it could just be on the next line. The "if" alerts the reader that there's a condition here..
I hope you'll agree that my second suggestion is pretty good :)
{{#each paragraphs}}
<p>{{this}}</p>
{{else}}
<p class="empty">No content</p>
{{/each}}
--- https://handlebarsjs.com/builtin_helpers.htmlSuppose it worked like that and you had this code:
for i in l:
A()
if B():
break
C()
else:
D()
Wouldn't that be equivalent to: for i in l:
A()
if B():
D()
break
C()
What your suggested else is doing is providing a place to put code to execute after a break in the loop, but we already have a place for such code: immediately in front of the break in the loop.The only place that comes to mind where this would be useful is situations where you there is more than one
if ...:
D()
break
in the loop. With your suggested kind of else, you could move all the D()'s to an else after the loop."else" is used with try blocks to indicate the exception didn't take place. It makes more sense there, because it follows the "except" sections.
> In 1991, “else” was the obvious choice because of the traditional way compilers implemented while-loops: pastebin.com/tY35CTJ4
That is, “if I haven’t finished the loop, GOTO the body.”
He says if he had a time machine he could tell Guido in the future we’re all using structured programming so no one will find “else” intuitive; call it “nobreak” instead: https://m.youtube.com/watch?v=OSGv2VnC0go#
Guido says if he had a time machine he would not have included the feature at all: https://mail.python.org/pipermail/python-ideas/2009-October/...
This whole thing was Knuth’s idea, not Guido’s: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.103...
So deprecate it with a runtime warning and remove it after some years.
I wonder if there are some statistics about its usage. I guess it's very low.
Just because it isn't named in a way people find intuitive doesn't mean it isn't useful.
Goto is considered harmful, but it's important to recognize the context of that decision rather than parroting the idea. It's not an obvious idea and if the discussion had happened today with modern languages[0] instead it's not at all obvious the same conclusions would be reached.
This write-up does a pretty fair job of translating the original paper to something more modern without a lot of bias. http://david.tribble.com/text/goto.html
[0]: Modern languages of course learned from this paper, so that's not a fair comparison. In fact golang contains a goto, but it behaves well according to Dijkstra's concerns and occasionally results in more readable code. I would be shocked if Dijkstra took any issue with go's implementation.
try:
import uvloop
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy)
except ImportError:
pass
Does the handler for if the try succeeds need to be in an else, separated from the success logic? try:
import uvloop
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy)
except ImportError:
pass
else:
print("well that didn't work") $ python3
>>> try:
1/0
except ZeroDivisionError:
print("exception")
else:
print("surprise")
exception
>>> try:
1/1
except ZeroDivisionError:
print("exception")
else:
print("normal")
1.0
normal
The choice of the name else is unfortunate because it's activated under the same circumstances of a then branch of an "if". Maybe its name should really be "then". That would also look like a .then of a JavaScript Promise, which would make sense nowadays and was obviously impossible to foresee back in 1991. However the name "else" makes sense if it refers to "except": it's what happens when no "except" fire. I still don't understand why it should be "else" for a "for" that succeeded: it should be a "then" there. $ python3
>>> for i in [1,2,3]:
print(i)
else:
print("completed")
1
2
3
completed
That should be a "then", but admittedly a very useless one.I agree that for the intended use case "else" is a better choice. It's like the else of the if that failed to find 2 in this code:
>>> for i in [1,2,3]:
if i == 2:
break
else:
print("we didn't find 2")
Still "else" doesn't hint much at what's going on, especially if the body of the loop is long. Maybe "nobreak" would make it immediately understandable to everybody? However in the best Python tradition I would make it very explicit, remove the feature from the language and use boolean flags. They are less compact but easier to understand than a feature that (probably) very few people know about. >>> we_found_2 = False # this is a valid assertion now
>>> for i in [1,2,3]:
if i == 2:
we_found_2 = True
break
if not we_found_2:
print("we didn't find 2")
That's also the only way to do it if there are multiple break in the loop on different conditions.print("well that's done")
It makes sense in the way that a language which is strongly biased to readability and consistent expectations makes sense. It is also how a language which minimizes programmer error makes sense.
For languages that refuse to remove anything, and thus suffer from unnecessary challenges, see C++.
However as far as programming is concerned, many things have been deprecated through transpilers and linters (for example "==" throws a warning in ESlint).
Cleaning up a language "makes no sense"?
Besides "could be useful" can be said for anything. Features should prove their usefulness (and in this case Guido regrets even adding it), we don't just keep them around in the off chance that they might be. That's hoarding.
It's one thing to make a change like this along with a bunch of other breaking changes, but there is no way this justifies breakage by itself.
No, I want a deprecation warning, and an eventual removal when for 4.0 lands.
>In what world does that sound like a good idea?
In a world where we don't pile crap upon crap forever, like with POSIX, X-Windows, or C++.
Everything has a cost. In maintenance, in documentation, in support, in limiting the design space or constraining further development for other reasons (performance, for example).
Nothing is truly free. Everything has to be balanced regarding costs and benefits.
"Programming languages should be designed not by piling feature on top of feature, but by removing the weaknesses and restrictions that make additional features appear necessary."
After the Python3 brouhaha, I think people are very cautious about making backwards incompatible changes to the language, even with a deprecation warning.
If it was done together with the rest of Python3 changes then it could have been approved, but not any more.
As Guido suggests, adding the warning to pylint and other style checkers, but keeping compatibility is much easier.
Running that on my personal projects folder gives me the following result:
for elses 60
for loops 1815
# modules 598
# skipped 2 for elses 0
for loops 428
# modules 673
# skipped 26
for elses 99
for loops 6058
# modules 2016
# skipped 286This will likely break some people's workflows, but honestly, it's the de-facto way of notifying users of a deprecation in many Python frameworks (Gunicorn did this recently with gunicorn-paster vs gunicorn --paste).
They could also include this in a "What's new in Python (4?)" since this is unlikely to be introduced in a minor version bump.
The proper way to handle this is of course to introduce a new semver major version. No need for deprecation warnings because incompatibility is expected.
Deprecating any feature without any strong reason makes me irritateted and I don't find any strong reason for doing so.
'for-else' never needs to be removed but the code cleanup will happen over time.
Sounds like if people were not already coding C, C++ and Pascal when Python was invented...
for item in list:
if search_condition:
found_item=item
break
else:
found_item=default_itemfound_item = next((item for item in list if search_condition), default_item)
that explains exactly what it does
However, I agree that the use of "else" is horribly confusing. When I teach "for-else" to students in my Python courses, they are often surprised by the functionality, and doubly surprised that the word "else" is used. I often joke that it would be a great feature, if only they had chosen something a bit more intuitive and memorable instead of "else", such as "if_we_exited_the_block_without_encountering_a_break". Slightly more seriously: While I realize that adding a keyword to Python isn't in the cards, a word such as "nobreak" would be easier for people to understand, and would stress the point of the functionality.
The fact that Emacs always tries to indent the "else" of a "for-else" to align with an "if" inside of the loop, rather than the "for" with which it matches, speaks volumes. I mean, if Emacs is confused, then how are we mere mortals supposed to remember?
So when I use "for-else", it's almost always in code that I am writing for myself, and that I don't expect others to read or maintain.
Don't forget that there's also "while-else", although I can't remember the last time I saw that actually used.
So now when I use it, I always put a comment next to it for myself and the next person:
for item in items:
if found:
break
else: # no-break
print('not found.')Except when the loop is infinite, or is broken prematurely, the loop guard will fail. Under for item in list, the implicit guard fails immediately when the list is empty. If the list isn't empty, it fails after the last item is iterated upon.
Thus here it also means "else if there are no more items to be processed".
But the "guard fails" and the else is executed on every normal loop exit, except if the exit was done with break.
for a in range( 1,3 ):
print( a )
else:
print( "end" )
gives: 1
2
end
It's an "alternative" only when there's break. In all other cases it is executed.Even naming it "nobreak" would be confusing as without break it is always executed at the end. The clearer naming would be approximately "on_every_nonbreak_end_of_for"
If there's anything I've learned, it's that just because a language has a feature, doesn't mean it should be used.
You shouldn't use a feature just because a language has it, you should use it because it's a simple, well-defined primitive that's less complex than alternatives.
It does though http://codepad.org/en4mzowh
A survey on this feature was done at Pycon 2011. Maybe things have changed since then, but at the time, most people didn't understand what for/else did.
I think it's a bad construct because, as many have mentioned, a lot of people make the commonsense interpretation that the else block will only execute if the for block did not.
Note, the link above come via Jeremy Avnet in the comments of this blog post.
- Does the else realize that the loop contains an if block?
- How does a break statement relate to the StopIteration exception?
- How does the else block run if the range wasn't, technically, empty?
That is exactly what it does. So when you explicitly break, the for body was executed. As for the poll, the first answer is not wrong. It's first option even, the question is vague enough. I wouldn't read that much into it, 33+24+9 (66%) people gave correct answer.
That being said, if you look around, pretty much everyone agrees it is a confusingly implemented feature. I don't think there's any question about that. But at the same time you can find explanations why it is named the way it is, and that can help to remember it better.
Maybe that is what is technically happening, I honestly don't know what that means.
I consider the body of a for statement to be execute for every loop, and for the loop to normally end when all values have been iterated. And else-clause is something that happens instead of the intended clause, which makes it very hard to fit in.
Reading through the comments here, there does seem to exist a mental model where the else makes sense. Something along the lines of
`if` there still is a value to assign to `i` do `loop-body`, `else` do `else-body`.
You can learn that, and it does make sense, but it is not the mental model the majority of people will build when they see a normal for loop in Python.
For break to be called explicitly it has to enter the loop body - there is no other way. The break only works in the loop body.
And the first answer is correct in the sense that it is not wrong (try it). The poll is simply flawed.
But it is VERY confusing.
Its very confusing, and poor design - IMHO - because the presence of an "else" block, in the typical case, generally indicates that the preceding block was never executed.
When it comes to while/for blocks in python, the keyword literally indicates the opposite - that the preceding block executed without hiccups - and so also execute this block.
Its a great feature - but there should have been another keyword for it: "also" (or something like that)
This is a case where the right answer is only obvious once it's been stated.
> Its a great feature - but there should have been another keyword for it: "also"
SmirkingRevenge - !@#$ing brilliant. You've stepped outside the box and presented an answer that will click as soon as it's read.
Someone make a PEP.
And I agree getting a new keyword for such a limited use-case feature would be difficult and doesn't feel likely.
I'd much prefer syntax like
da_thing = for ook in blahblah:
if oook == something:
break oook
if da_thing is None:
print ("Not found")
else:
print ('Found', da_thing) da_thing = None
for oook in blahblah:
if oook == something:
da_thing = oook
break
if da_thing is None:
print ("Not found")
else:
print ('Found', da_thing)
But it does not work if you sometimes want to find `None`, so better would be something like: da_thing = sentinel = object()
for oook in blahblah:
if oook == something:
da_thing = oook
break
if da_thing is sentinel:
print ("Not found")
else:
print ('Found', da_thing)JavaScript takes this to an even more ludicrous place by having both 'null' and 'undefined', putting them close to each other in the truth table, and allowing them both as rvalues.
Does anyone know of any procedural programming languages (or at least languages that encourage mutation, initialization-without-assignment, and rebinding) that have a) a special value for 'never accessed' and b) do not allow that value to be set, only read?
Seems like bad usability.
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.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:
...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).
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 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.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.
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.
try:
result = ''
while (b = next(file_bytes_iterator)) != '\0':
result += b
# maybe parse more of the file here
except StopIteration:
throw ValueError("Unexpected end of file") 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".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.
for x in sorted_list:
if x == to_find:
print("found")
break # we've found a match
elif to_find > x:
break # match is not possible
else:
print("not found)Also, nice counterexample.
First of all, the core of a linear search loop always has two exits: the break and the normal (unsuccessful) loop exit.
But in a lookup-or-insert pattern, a regular for loop has two possible states that the system can be in at the end of the loop. With a for-else loop, you can put the or-insert part into the else block, so that there is a simpler invariant: after the loop construct, the element always exists and has been found.
I was honestly surprised by all the negativity in this thread. To offer an alternative opinion, there have been plenty of times when I missed having for-else while writing C.
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"?
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.
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?
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.
If you naively banish break and continue, but then the programmer has to work around your banishment by adding one more state variable, and assigning to that variable, you have not gained anything. You've pushed down a bulge in the carpet, only to have it pop up elsewhere.
Assignment and goto are bedfellows, which is why purely functional programming banishes both. (Goto, at its essence, is in fact an assignment to the program counter.)
I don't think that I've yet come across a use of break/continue that can't be pretty cleanly refactored into a helper method like
def my_filter(items):
for x in items:
if cond(x):
yield x
for x in my_filter(base_list):
process(x)
This ends up being pretty clean and describes exactly what/why you're filtering things, plus as the conditions change you can test things individually.Escaping in two different ways really isn't a problem for that kind of analysis. You just have to make sure that the post-condition is satisfied at each exit. For/else is actually a great construct for that, where the else branch is used to achieve the same post-condition as after a break, which would be more difficult if there were no difference between the loop exiting with a break or by exhausting the iterator.
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."I would like to see this in Python:
for i in []:
print(i)
empty:
print("Sequence was empty!")It's great for controlling iteration in a nested loop:
for item in report():
for event in in item['events']:
if event in events_we_care_about:
break
else:
continue
act_upon(item)
I realize it's a trivial example that could be written without a nested loop, but there are often good reasons for nested loops and littering them with control variables and if statements gets messy quick. I have always found for-else to produce more elegant code.Your criticisms may be quite valid, I just wanted to share my surprise.
for item in report():
for event in item['events']:
if event in events_we_care_about:
act_upon(event)
break for item in report():
if any(event in events_we_care_about
for event in item['events']):
act_upon(item)
But obviously the features that enable this form didn't exist when for...else was added to the language.How about for-finally? It’s a hard thing to name correctly, but to me this seems to be slightly more intuitive if you’ve never seen the feature before.
I had to call an API for it to load data into a cache, but sometimes this wouldn't work so I made it try up to 3 times, otherwise it would show an error. So something like:
for i in range(3):
if load_cache():
log("success")
break
else:
log("failure")
Else made total sense here.As I recall, he said added it because the implementation structure of the "for" code matched the implementation structure of the "if" code, and he felt that 'for' could use an 'else'.
[1]: https://docs.python.org/3.6/reference/compound_stmts.html#wh...
while x < 2:
...
else:
print('>= 2')
is the same as while x < 2:
...
print('>= 2')
unless you use a break. Just seems unnecessarily confusing to me. I have used for ... else though.Quoting documentation: "A break statement executed in the first suite terminates the loop without executing the else clause’s suite."
But indeed, in my experience, while+else are much less common that for+else. I'm not sure why.
The 'try' block contains code which might raise an exception.
It can be followed by zero or more 'except' blocks, each of which indicates the type(s) of exception it will catch and contains code to execute in the event of that type of exception.
The 'except' block(s) can be followed by one 'else' block, which contains code to execute in the event no exception was raised by the code in the 'try' block.
And then there is optionally a 'finally' block, containing code that must be executed in all circumstances.
I would rather something that captures the loop state itself as an operable object, such as:
with for as loop:
for x in my_list:
...
if not loop.broken:
...To me and many others, having an else associated to an if that ends in break/continue/return is a code smell, because it misleadingly suggests that there are two possible paths of control flow that reach the code after the if/else, when in reality you can only reach that point through the else block.
for _ in range(3):
success = do_something()
if success:
break
else:
raise FailedToDoSomething
I generally like this feature of the for loop and it would be a shame if it was removed. item = next((item for item in container if search_something(item)), None)
if item:
process(item)
else:
not_found_in_container()
instead of as suggested in the blog post: for item in container:
if search_something(item):
# Found it!
process(item)
break
else:
# Didn't find anything..
not_found_in_container() sentinel = object()
item = next((item for item in container if search_something(item)), sentinel)
if item is not sentinel:
process(item)
else:
not_found_in_container()
... because item could be None or another falsy value.Go-lang decided against exceptions in part because they are equivalent to gotos.
for i in range(10):
for j in range(10):
if condition(i,j):
dosomething
break
else:
continue
break
It's a pattern that ensure that dosometing
is run at most once by "propagating" the
break to the outer loop.The alternative is use a function and do a return.
Any though of this ?
(x, y) for x in range(10) for y in range(10)
if condition(i,j)
dosomething;
break;
Edit, ("zip" is not the outer product... ) for x, y in itertools.product(range(10), repeat=2):
if condition(x, y):
dosomething(x, y)
break
Alternatively, you can use Python list comprehensions: prodlist = [x, y for x in range(10) for y in range(10)]
for x, y in prodlist:
if condition(x, y):
dosomething(x, y)
break
Overall, I still prefer refactoring into a function and using return, as the GP comment suggested.[1] https://docs.python.org/3/library/itertools.html#itertools.p...
But there clearly is a body of knowledge which you can discover yourself: you can interview candidates, read discussions on SO, read library code, and you will find some techniques are commonly used, and some are rarely used.
That distribution of what's known is changing over time regardless of what we do, because our peers are constantly learning and studying at different rates.
This feature has been around for years, and is so rarely used that only language mavens know about it. Thus, you can observe that this is a failed feature and avoid using it.
We can always reevaluate if people understand it later on, since that could change.
In a way I find your comment contradictory: on the one hand, you appreciate and embrace that the body of common knowledge can change. On the other hand, you use the current state of common knowledge as an argument against doing things that can improve the common knowledge.
Here’s the python language reference from python version 1.4 (1996), http://web.archive.org/web/19970606191447/http://www.python....
> The expression list is evaluated once; it should yield a sequence. The suite is then executed once for each item in the sequence, in the order of ascending indices. Each item in turn is assigned to the target list using the standard rules for assignments, and then the suite is executed. When the items are exhausted (which is immediately when the sequence is empty), the suite in the else clause, if present, is executed, and the loop terminates.
> A break statement executed in the first suite terminates the loop without executing the else clause's suite. A continue statement executed in the first suite skips the rest of the suite and continues with the next item, or with the else clause if there was no next item.
* * *
Here’s the tutorial, http://web.archive.org/web/19970606183615/http://www.python....
> The break statement, like in C, breaks out of the smallest enclosing for or while loop.
> The continue statement, also borrowed from C, continues with the next iteration of the loop.
> Loop statements may have an else clause; it is executed when the loop terminates through exhaustion of the list (with for) or when the condition becomes false (with while), but not when the loop is terminated by a break statement. [... and then an example ...]
You decide with experience. When other people read my code, this is probably the feature that surprises them most. Other features that they haven't seen before usually make sense from context. This one is surprising enough that I often forget whether it executes if there is a break or if there isn't a break.
When explaining and remembering what the syntax means dwarfs the effort saved by using the syntax, it's a niche syntax trick.
That and the absence of do-while are the cause of a good deal of non-DRY python around loops.
For some reason I often forget to use it though.
Always KISS.
How about "ifcompleted"
It certainly shouldn't be used when unclear, just like any feature. There's no reason this feature's sensible use cases should be as niche as they currently are.
Questions about Python's syntax are anything but impossible to look up. Python has excellent documentation around this, and the steps to get to the right part of the documentation are pretty straight-forward:
1. Start at the main page, https://docs.python.org/
2. Click "Language Reference"; note the subtitle that clearly hints this is what we want: "describes syntax and language elements"
3. We are interested in for loops, so we click "8.3. The for statement"; the resulting page describes the behavior.
The code in your example is how it reads, but that's not how it works. The "obvious" reading is wrong, so it's obviously not "immediately obvious".
This is what it's actually doing:
found = False
for l in get_list():
if ...:
found = True
break
if not found:
...https://stackoverflow.com/questions/9979970/why-does-python-...
Tl;dr my highly opinionated opinion is that one should never use the construct
The For/While-else does exactly what I expect. I'm not sure why others find it confusing.
The Try-else does make some code more readable but is quite rare I would say.
if len(items) == 0:
print(y)
else:
for item in items:
print(item)
Which means there's a whole extra implicit if. The for in Python is really this: it = iter(items)
while True:
try:
item = next(it)
except StopIteration:
print("else")
break
print(item)
Where that try/except is like "if there is a next item do this, else do this", hence: for item in items:
print(item)
else:
print("else")He suggests using a helper function with an early exit or setting a result variable and using a break in your loop instead.