I've noticed the following code is legal in Python. My question is why? Is there a specific reason?
n = 5
while n != 0:
print n
n -= 1
else:
print "what the..."
Many beginners accidentally stumble on this syntax when they try to put an if/else block inside of a while or for loop, and don't indent the else properly. The solution is to make sure the else block lines up with the if, assuming that it was your intent to pair them. This question explains why it didn't cause a syntax error, and what the resulting code means. See also I'm getting an IndentationError. How do I fix it?, for the cases where there is a syntax error reported.
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.
One way to think about it is as an if/else construct with respect to the condition:
if condition:
handle_true()
else:
handle_false()
is analogous to the looping construct:
while condition:
handle_true()
else:
# condition is false now, handle and go on with the rest of the program
handle_false()
An example might be along the lines of:
while value < threshold:
if not process_acceptable_value(value):
# something went wrong, exit the loop; don't pass go, don't collect 200
break
value = update(value)
else:
# value >= threshold; pass go, collect 200
handle_threshold_reached()
The else clause is executed if you exit a block normally, by hitting the loop condition or falling off the bottom of a try block. It is not executed if you break or return out of a block, or raise an exception. It works for not only while and for loops, but also try blocks.
You typically find it in places where normally you would exit a loop early, and running off the end of the loop is an unexpected/unusual occasion. For example, if you're looping through a list looking for a value:
for value in values:
if value == 5:
print "Found it!"
break
else:
print "Nowhere to be found. :-("
Allow me to give an example on why to use this else-clause. But:
my point is now better explained in Leo’s answer
I use a for- instead of a while-loop, but else works similar (executes unless break was encountered)
there are better ways to do this (e.g. wrapping it into a function or raising an exception)
Breaking out of multiple levels of looping
Here is how it works: the outer loop has a break at the end, so it would only be executed once. However, if the inner loop completes (finds no divisor), then it reaches the else statement and the outer break is never reached. This way, a break in the inner loop will break out of both loops, rather than just one.
for k in [2, 3, 5, 7, 11, 13, 17, 25]:
for m in range(2, 10):
if k == m:
continue
print 'trying %s %% %s' % (k, m)
if k % m == 0:
print 'found a divisor: %d %% %d; breaking out of loop' % (k, m)
break
else:
continue
print 'breaking another level of loop'
break
else:
print 'no divisor could be found!'
The else-clause is executed when the while-condition evaluates to false.
From the documentation:
The while statement is used for repeated execution as long as an expression is true:
while_stmt ::= "while" expression ":" suite
["else" ":" suite]
This repeatedly tests the expression and, if it is true, executes the first suite; if the expression is false (which may be the first time it is tested) the suite of 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 goes back to testing the expression.
The else clause is only executed when the while-condition becomes false.
Here are some examples:
Example 1: Initially the condition is false, so else-clause is executed.
i = 99999999
while i < 5:
print(i)
i += 1
else:
print('this')
OUTPUT:
this
Example 2: The while-condition i < 5 never became false because i == 3 breaks the loop, so else-clause was not executed.
i = 0
while i < 5:
print(i)
if i == 3:
break
i += 1
else:
print('this')
OUTPUT:
0
1
2
3
Example 3: The while-condition i < 5 became false when i was 5, so else-clause was executed.
i = 0
while i < 5:
print(i)
i += 1
else:
print('this')
OUTPUT:
0
1
2
3
4
this
My answer will focus on WHEN we can use while/for-else.
At the first glance, it seems there is no different when using
while CONDITION:
EXPRESSIONS
print 'ELSE'
print 'The next statement'
and
while CONDITION:
EXPRESSIONS
else:
print 'ELSE'
print 'The next statement'
Because the print 'ELSE' statement seems always executed in both cases (both when the while loop finished or not run).
Then, it's only different when the statement print 'ELSE' will not be executed.
It's when there is a breakinside the code block under while
In [17]: i = 0
In [18]: while i < 5:
print i
if i == 2:
break
i = i +1
else:
print 'ELSE'
print 'The next statement'
....:
0
1
2
The next statement
If differ to:
In [19]: i = 0
In [20]: while i < 5:
print i
if i == 2:
break
i = i +1
print 'ELSE'
print 'The next statement'
....:
0
1
2
ELSE
The next statement
return is not in this category, because it does the same effect for two above cases.
exception raise also does not cause difference, because when it raises, where the next code will be executed is in exception handler (except block), the code in else clause or right after the while clause will not be executed.
I know this is old question but...
As Raymond Hettinger said, it should be called while/no_break instead of while/else.
I find it easy to understeand if you look at this snippet.
n = 5
while n > 0:
print n
n -= 1
if n == 2:
break
if n == 0:
print n
Now instead of checking condition after while loop we can swap it with else and get rid of that check.
n = 5
while n > 0:
print n
n -= 1
if n == 2:
break
else: # read it as "no_break"
print n
I always read it as while/no_break to understand the code and that syntax makes much more sense to me.
thing = 'hay'
while thing:
if thing == 'needle':
print('I found it!!') # wrap up for break
break
thing = haystack.next()
else:
print('I did not find it.') # wrap up for no-break
The possibly unfortunately named else-clause is your place to wrap up from loop-exhaustion without break.
You can get by without it if
you break with return or raise → the entire code after the call or try is your no-break place
you set a default before while (e.g. found = False)
but it might hide bugs the else-clause knows to avoid
If you use a multi-break with non-trivial wrap-up, you should use a simple assignment before break, an else-clause assignment for no-break, and an if-elif-else or match-case to avoid repeating non-trival break handling code.
Note: the same applies to for thing in haystack:
Else is executed if while loop did not break.
I kinda like to think of it with a 'runner' metaphor.
The "else" is like crossing the finish line, irrelevant of whether you started at the beginning or end of the track. "else" is only not executed if you break somewhere in between.
runner_at = 0 # or 10 makes no difference, if unlucky_sector is not 0-10
unlucky_sector = 6
while runner_at < 10:
print("Runner at: ", runner_at)
if runner_at == unlucky_sector:
print("Runner fell and broke his foot. Will not reach finish.")
break
runner_at += 1
else:
print("Runner has finished the race!") # Not executed if runner broke his foot.
Main use cases is using this breaking out of nested loops or if you want to run some statements only if loop didn't break somewhere (think of breaking being an unusual situation).
For example, the following is a mechanism on how to break out of an inner loop without using variables or try/catch:
for i in [1,2,3]:
for j in ['a', 'unlucky', 'c']:
print(i, j)
if j == 'unlucky':
break
else:
continue # Only executed if inner loop didn't break.
break # This is only reached if inner loop 'breaked' out since continue didn't run.
print("Finished")
# 1 a
# 1 b
# Finished
The else: statement is executed when and only when the while loop no longer meets its condition (in your example, when n != 0 is false).
So the output would be this:
5
4
3
2
1
what the...
Suppose you've to search an element x in a single linked list
def search(self, x):
position = 1
p =self.start
while p is not None:
if p.info == x:
print(x, " is at position ", position)
return True
position += 1
p = p.link
else:
print(x, "not found in list")
return False
So if while conditions fails else will execute, hope it helps!
The better use of 'while: else:' construction in Python should be if no loop is executed in 'while' then the 'else' statement is executed. The way it works today doesn't make sense because you can use the code below with the same results...
n = 5
while n != 0:
print n
n -= 1
print "what the..."
As far as I know the main reason for adding else to loops in any language is in cases when the iterator is not on in your control. Imagine the iterator is on a server and you just give it a signal to fetch the next 100 records of data. You want the loop to go on as long as the length of the data received is 100. If it is less, you need it to go one more times and then end it. There are many other situations where you have no control over the last iteration. Having the option to add an else in these cases makes everything much easier.
I understand how this construct works:
for i in range(10):
print(i)
if i == 9:
print("Too big - I'm giving up!")
break
else:
print("Completed successfully")
But I don't understand why else is used as the keyword here, since it suggests the code in question only runs if the for block does not complete, which is the opposite of what it does! No matter how I think about it, my brain can't progress seamlessly from the for statement to the else block. To me, continue or continuewith would make more sense (and I'm trying to train myself to read it as such).
I'm wondering how Python coders read this construct in their head (or aloud, if you like). Perhaps I'm missing something that would make such code blocks more easily decipherable?
This question is about the underlying design decision, i.e. why it is useful to be able to write this code. See also Else clause on Python while statement for the specific question about what the syntax means.
A common construct is to run a loop until something is found and then to break out of the loop. The problem is that if I break out of the loop or the loop ends I need to determine which case happened. One method is to create a flag or store variable that will let me do a second test to see how the loop was exited.
For example assume that I need to search through a list and process each item until a flag item is found and then stop processing. If the flag item is missing then an exception needs to be raised.
Using the Python for...else construct you have
for i in mylist:
if i == theflag:
break
process(i)
else:
raise ValueError("List argument missing terminal flag.")
Compare this to a method that does not use this syntactic sugar:
flagfound = False
for i in mylist:
if i == theflag:
flagfound = True
break
process(i)
if not flagfound:
raise ValueError("List argument missing terminal flag.")
In the first case the raise is bound tightly to the for loop it works with. In the second the binding is not as strong and errors may be introduced during maintenance.
It's a strange construct even to seasoned Python coders. When used in conjunction with for-loops it basically means "find some item in the iterable, else if none was found do ...". As in:
found_obj = None
for obj in objects:
if obj.key == search_key:
found_obj = obj
break
else:
print('No object found.')
But anytime you see this construct, a better alternative is to either encapsulate the search in a function:
def find_obj(search_key):
for obj in objects:
if obj.key == search_key:
return obj
Or use a list comprehension:
matching_objs = [o for o in objects if o.key == search_key]
if matching_objs:
print('Found {}'.format(matching_objs[0]))
else:
print('No object found.')
It is not semantically equivalent to the other two versions, but works good enough in non-performance critical code where it doesn't matter whether you iterate the whole list or not. Others may disagree, but I personally would avoid ever using the for-else or while-else blocks in production code.
See also [Python-ideas] Summary of for...else threads
There's an excellent presentation by Raymond Hettinger, titled Transforming Code into Beautiful, Idiomatic Python, in which he briefly addresses the history of the for ... else construct. The relevant section is "Distinguishing multiple exit points in loops" starting at 15:50 and continuing for about three minutes. Here are the high points:
The for ... else construct was devised by Donald Knuth as a replacement for certain GOTO use cases;
Reusing the else keyword made sense because "it's what Knuth used, and people knew, at that time, all [for statements] had embedded an if and GOTO underneath, and they expected the else;"
In hindsight, it should have been called "no break" (or possibly "nobreak"), and then it wouldn't be confusing.*
So, if the question is, "Why don't they change this keyword?" then Cat Plus Plus probably gave the most accurate answer – at this point, it would be too destructive to existing code to be practical. But if the question you're really asking is why else was reused in the first place, well, apparently it seemed like a good idea at the time.
Personally, I like the compromise of commenting # no break in-line wherever the else could be mistaken, at a glance, as belonging inside the loop. It's reasonably clear and concise. This option gets a brief mention in the summary that Bjorn linked at the end of his answer:
For completeness, I should mention that with a slight change in
syntax, programmers who want this syntax can have it right now:
for item in sequence:
process(item)
else: # no break
suite
* Bonus quote from that part of the video: "Just like if we called lambda makefunction, nobody would ask, 'What does lambda do?'"
To make it simple, you can think of it like that;
If it encounters the break command in the for loop, the else part will not be called.
If it does not encounter the break command in the for loop, the else part will be called.
In other words, if for loop iteration is not "broken" with break, the else part will be called.
Because they didn't want to introduce a new keyword to the language. Each one steals an identifier and causes backwards compatibility problems, so it's usually a last resort.
I think documentation has a great explanation of else, continue
[...] 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."
Source: Python 2 docs: Tutorial on control flow
The easiest way I found to 'get' what the for/else did, and more importantly, when to use it, was to concentrate on where the break statement jumps to. The For/else construct is a single block. The break jumps out of the block, and so jumps 'over' the else clause. If the contents of the else clause simply followed the for clause, it would never be jumped over, and so the equivalent logic would have to be provided by putting it in an if. This has been said before, but not quite in these words, so it may help somebody else. Try running the following code fragment. I'm wholeheartedly in favour of the 'no break' comment for clarity.
for a in range(3):
print(a)
if a==4: # change value to force break or not
break
else: #no break +10 for whoever thought of this decoration
print('for completed OK')
print('statement after for loop')
EDIT - I notice this question is still running
Second better thoughts ...
The 'no break' comment is a negative. It's so much easier to understand a positive assertion, and that is that the for iterable was exhausted.
for a in range(3):
print(a)
if a==4: # change value to force break or not
print('ending for loop with a break')
break
else: # for iterable exhausted
print('ending for loop as iterable exhausted')
print('for loop ended one way or another')
That also reinforces this interpretation
if iterable_supplies_a_value:
run_the_for_with_that_value
else:
do_something_else
I read it something like:
If still on the conditions to run the loop, do stuff, else do something else.
Since the technical part has been pretty much answered, my comment is just in relation with the confusion that produce this recycled keyword.
Being Python a very eloquent programming language, the misuse of a keyword is more notorious. The else keyword perfectly describes part of the flow of a decision tree, "if you can't do this, (else) do that". It's implied in our own language.
Instead, using this keyword with while and for statements creates confusion. The reason, our career as programmers has taught us that the else statement resides within a decision tree; its logical scope, a wrapper that conditionally return a path to follow. Meanwhile, loop statements have a figurative explicit goal to reach something. The goal is met after continuous iterations of a process.
if / else indicate a path to follow. Loops follow a path until the "goal" is completed.
The issue is that else is a word that clearly define the last option in a condition. The semantics of the word are both shared by Python and Human Language. But the else word in Human Language is never used to indicate the actions someone or something will take after something is completed. It will be used if, in the process of completing it, an issue rises (more like a break statement).
At the end, the keyword will remain in Python. It's clear it was mistake, clearer when every programmer tries to come up with a story to understand its usage like some mnemonic device. I'd have loved if they have chosen instead the keyword then. I believe that this keyword fits perfectly in that iterative flow, the payoff after the loop.
It resembles that situation that some child has after following every step in assembling a toy: And THEN what Dad?
Great answers are:
this which explain the history, and
this gives the right
citation to ease yours translation/understanding.
My note here comes from what Donald Knuth once said (sorry can't find reference) that there is a construct where while-else is indistinguishable from if-else, namely (in Python):
x = 2
while x > 3:
print("foo")
break
else:
print("boo")
has the same flow (excluding low level differences) as:
x = 2
if x > 3:
print("foo")
else:
print("boo")
The point is that if-else can be considered as syntactic sugar for while-else which has implicit break at the end of its if block. The opposite implication, that while loop is extension to if, is more common (it's just repeated/looped conditional check), because if is often taught before while. However that isn't true because that would mean else block in while-else would be executed each time when condition is false.
To ease your understanding think of it that way:
Without break, return, etc., loop ends only when condition is no longer true and in such case else block will also execute once. In case of Python for you must consider C-style for loops (with conditions) or translate them to while.
Another note:
Premature break, return, etc. inside loop makes impossible for condition to become false because execution jumped out of the loop while condition was true and it would never come back to check it again.
I'm wondering how Python coders read this construct in their head (or aloud, if you like).
I simply think in my head:
"else no break was encountered..."
That's it!
This is because the else clause executes only if a break statement was NOT encountered in the for loop.
Reference:
See here: https://book.pythontips.com/en/latest/for_-_else.html#else-clause (emphasis added, and "not" changed to "NOT"):
for loops also have an else clause which most of us are unfamiliar with. The else clause executes after the loop completes normally. This means that the loop did NOT encounter a break statement.
That being said, I recommend against using this unusual feature of the language. Don't use the else clause after a for loop. It's confusing to most people, and just slows down their ability to read and understand the code.
I read it like "When the iterable is exhausted completely, and the execution is about to proceed to the next statement after finishing the for, the else clause will be executed." Thus, when the iteration is broken by break, this will not be executed.
I agree, it's more like an 'elif not [condition(s) raising break]'.
I know this is an old thread, but I am looking into the same question right now, and I'm not sure anyone has captured the answer to this question in the way I understand it.
For me, there are three ways of "reading" the else in For... else or While... else statements, all of which are equivalent, are:
else == if the loop completes normally (without a break or error)
else == if the loop does not encounter a break
else == else not (condition raising break) (presumably there is such a condition, or you wouldn't have a loop)
So, essentially, the "else" in a loop is really an "elif ..." where '...' is (1) no break, which is equivalent to (2) NOT [condition(s) raising break].
I think the key is that the else is pointless without the 'break', so a for...else includes:
for:
do stuff
conditional break # implied by else
else not break:
do more stuff
So, essential elements of a for...else loop are as follows, and you would read them in plainer English as:
for:
do stuff
condition:
break
else: # read as "else not break" or "else not condition"
do more stuff
As the other posters have said, a break is generally raised when you are able to locate what your loop is looking for, so the else: becomes "what to do if target item not located".
Example
You can also use exception handling, breaks, and for loops all together.
for x in range(0,3):
print("x: {}".format(x))
if x == 2:
try:
raise AssertionError("ASSERTION ERROR: x is {}".format(x))
except:
print(AssertionError("ASSERTION ERROR: x is {}".format(x)))
break
else:
print("X loop complete without error")
Result
x: 0
x: 1
x: 2
ASSERTION ERROR: x is 2
----------
# loop not completed (hit break), so else didn't run
Example
Simple example with a break being hit.
for y in range(0,3):
print("y: {}".format(y))
if y == 2: # will be executed
print("BREAK: y is {}\n----------".format(y))
break
else: # not executed because break is hit
print("y_loop completed without break----------\n")
Result
y: 0
y: 1
y: 2
BREAK: y is 2
----------
# loop not completed (hit break), so else didn't run
Example
Simple example where there no break, no condition raising a break, and no error are encountered.
for z in range(0,3):
print("z: {}".format(z))
if z == 4: # will not be executed
print("BREAK: z is {}\n".format(y))
break
if z == 4: # will not be executed
raise AssertionError("ASSERTION ERROR: x is {}".format(x))
else:
print("z_loop complete without break or error\n----------\n")
Result
z: 0
z: 1
z: 2
z_loop complete without break or error
----------
The else keyword can be confusing here, and as many people have pointed out, something like nobreak, notbreak is more appropriate.
In order to understand for ... else ... logically, compare it with try...except...else, not if...else..., most of python programmers are familiar with the following code:
try:
do_something()
except:
print("Error happened.") # The try block threw an exception
else:
print("Everything is find.") # The try block does things just find.
Similarly, think of break as a special kind of Exception:
for x in iterable:
do_something(x)
except break:
pass # Implied by Python's loop semantics
else:
print('no break encountered') # No break statement was encountered
The difference is python implies except break and you can not write it out, so it becomes:
for x in iterable:
do_something(x)
else:
print('no break encountered') # No break statement was encountered
Yes, I know this comparison can be difficult and tiresome, but it does clarify the confusion.
Codes in else statement block will be executed when the for loop was not be broke.
for x in xrange(1,5):
if x == 5:
print 'find 5'
break
else:
print 'can not find 5!'
#can not find 5!
From the docs: break and continue Statements, and else Clauses on Loops
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. This is exemplified by the following loop, which searches for prime numbers:
>>> for n in range(2, 10):
... for x in range(2, n):
... if n % x == 0:
... print(n, 'equals', x, '*', n//x)
... break
... else:
... # loop fell through without finding a factor
... print(n, 'is a prime number')
...
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3
(Yes, this is the correct code. Look closely: the else clause belongs to the for loop, not the if statement.)
When used with a loop, the else clause has more in common with the else clause of a try statement than it does that of if statements: a try statement’s else clause runs when no exception occurs, and a loop’s else clause runs when no break occurs. For more on the try statement and exceptions, see Handling Exceptions.
The continue statement, also borrowed from C, continues with the next iteration of the loop:
>>> for num in range(2, 10):
... if num % 2 == 0:
... print("Found an even number", num)
... continue
... print("Found a number", num)
Found an even number 2
Found a number 3
Found an even number 4
Found a number 5
Found an even number 6
Found a number 7
Found an even number 8
Found a number 9
Here's a way to think about it that I haven't seen anyone else mention above:
First, remember that for-loops are basically just syntactic sugar around while-loops. For example, the loop
for item in sequence:
do_something(item)
can be rewritten (approximately) as
item = None
while sequence.hasnext():
item = sequence.next()
do_something(item)
Second, remember that while-loops are basically just repeated if-blocks! You can always read a while-loop as "if this condition is true, execute the body, then come back and check again".
So while/else makes perfect sense: It's the exact same structure as if/else, with the added functionality of looping until the condition becomes false instead of just checking the condition once.
And then for/else makes perfect sense too: because all for-loops are just syntactic sugar on top of while-loops, you just need to figure out what the underlying while-loop's implicit conditional is, and then the else corresponds to when that condition becomes False.
for i in range(3):
print(i)
if i == 2:
print("Too big - I'm giving up!")
break;
else:
print("Completed successfully")
"else" here is crazily simple, just mean
1, "if for clause is completed"
for i in range(3):
print(i)
if i == 2:
print("Too big - I'm giving up!")
break;
if "for clause is completed":
print("Completed successfully")
It's wielding to write such long statements as "for clause is completed", so they introduce "else".
else here is a if in its nature.
2, However, How about for clause is not run at all
In [331]: for i in range(0):
...: print(i)
...:
...: if i == 9:
...: print("Too big - I'm giving up!")
...: break
...: else:
...: print("Completed successfully")
...:
Completed successfully
So it's completely statement is logic combination:
if "for clause is completed" or "not run at all":
do else stuff
or put it this way:
if "for clause is not partially run":
do else stuff
or this way:
if "for clause not encounter a break":
do else stuff
Here's another idiomatic use case besides searching. Let's say you wanted to wait for a condition to be true, e.g. a port to be open on a remote server, along with some timeout. Then you could utilize a while...else construct like so:
import socket
import time
sock = socket.socket()
timeout = time.time() + 15
while time.time() < timeout:
if sock.connect_ex(('127.0.0.1', 80)) is 0:
print('Port is open now!')
break
print('Still waiting...')
else:
raise TimeoutError()
I was just trying to make sense of it again myself. I found that the following helps!
• Think of the else as being paired with the if inside the loop (instead of with the for) - if condition is met then break the loop, else do this - except it's one else paired with multiple ifs!
• If no ifs were satisfied at all, then do the else.
• The multiple ifs can also actually be thought of as if-elifs!
for i in range(10):
print(i)
if i == 9:
print("Too big - I'm giving up!")
break;
else:
print("Completed successfully")
break keyword is used to end the loop. if the i = 9 then the loop will end. while any if conditions did not much the satisfaction, then the else will do the rest part.
The else clause executes after the loop completes normally. This means The :==>
else block just after for/while is executed only when the loop is NOT terminated by a break statement
for item in lista:
if(obj == item ):
print("if True then break will run and else not run")
break;
else:
print("in else => obj not fount ")
You could think of it like,
else as in the rest of the stuff, or the other stuff, that wasn't done in the loop.
A loop's else branch executes once, regardless of whether the loop enters its body or not, unless the loop body is entered but does not finish. That is, inside the loop a break or return statement is encountered.
my_list = []
for i in my_list:
print(i, end=',')
else:
print('loop did not enter')
##################################
for i in range(1,6,1):
print(i, end=',')
else:
print('loop completed successfully:', i)
##################################
for i in range(1,6,1):
if i == 3:
print('loop did not finish:', i)
break
print(i, end=',')
else:
print('else:', i)
Output:
loop did not enter
1,2,3,4,5,loop completed successfully: 5
1,2,loop did not finish: 3
It's the same for while-else.
import random
random.seed(8)
i = 100
while i < 90:
print(i, end=',')
i = random.randint(0,100)
else:
print('loop did not enter:', i)
##################################
i = 25
while i < 90:
print(i, end=',')
i = random.randint(0,100)
else:
print('loop completed successfully:', i)
##################################
i = 25
while i < 90:
if i % 10 == 0:
print('loop did not finish:', i)
break
print(i, end=',')
i = random.randint(0,100)
else:
print('else:', i)
Output:
loop did not enter: 100
25,29,47,48,16,24,loop completed successfully: 90
25,5,loop did not finish: 10
I consider the structure as for (if) A else B, and for(if)-else is a special if-else, roughly. It may help to understand else.
A and B is executed at most once, which is the same as if-else structure.
for(if) can be considered as a special if, which does a loop to try to meet the if condition. Once the if condition is met, A and break; Else, B.
I'm reading about the yield keyword in python, and trying to understand running this sample:
def countfrom(n):
while True:
print "before yield"
yield n
n += 1
print "after yield"
for i in countfrom(10):
print "enter for loop"
if i <= 20:
print i
else:
break
The output is:
before yield
enter for loop
10
after yield
before yield
enter for loop
11
after yield
before yield
enter for loop
12
after yield
before yield
enter for loop
13
after yield
before yield
enter for loop
14
after yield
before yield
enter for loop
15
after yield
before yield
enter for loop
16
after yield
before yield
enter for loop
17
after yield
before yield
enter for loop
18
after yield
before yield
enter for loop
19
after yield
before yield
enter for loop
20
after yield
before yield
enter for loop
It looks like the yield will return the specified value, and will continue runnning the function till the end (in a parallel thread, maybe). Is my understand correct?
If you could answer this without mentioning "generators", I would be thankful, because I'm trying to understand one at a time.
You can think of it as if the function which yields simply "pauses" when it comes across the yield. The next time you call it, it will resume after the yield keeping the state that it was in when it left.
No, there is only a single thread.
Each iteration of the for loop runs your countFrom function until it yields something, or returns. After the yield, the body of the for loop runs again and then, when a new iteration starts, the countFrom function picks up exactly where it left off and runs again until it yields (or returns).
This modified version of your example will helpfully make it clearer what path execution takes.
def countfrom(n):
while n <= 12:
print "before yield, n = ", n
yield n
n += 1
print "after yield, n = ", n
for i in countfrom(10):
print "enter for loop, i = ", i
print i
print "end of for loop iteration, i = ", i
Output
before yield, n = 10
enter for loop, i = 10
10
end of for loop iteration, i = 10
after yield, n = 11
before yield, n = 11
enter for loop, i = 11
11
end of for loop iteration, i = 11
after yield, n = 12
before yield, n = 12
enter for loop, i = 12
12
end of for loop iteration, i = 12
after yield, n = 13
..you cannot explain the meaning of the yield statement without mentioning generators; it would be like trying to explain what a stone is without mentioning rock. That is: the yield statement is the one responsible to transform a normal function into a generator.
While you find it well documented here: http://docs.python.org/reference/simple_stmts.html#the-yield-statement
..the brief explaination of it is:
When a function using the yield statement is called, it returns a "generator iterator", having a .next() method (the standard for iterable objects)
Each time the .next() method of the generator is called (eg. by iterating the object with a for loop), the function is called until the first yield is encountered. Then the function execution is paused and a value is passed as return value of the .next() method.
The next time .next() is called, the function execution is resumed until the next yield, etc. until the function returns something.
Some advantages in doing this are:
less memory usage since memory is allocated just for the currently yielded value, not the whole list of returned values (as it would be by returning a list of values)
"realtime" results return, as they are produced can be passed to the caller without waiting for the generation end (i used that to return output from a running process)
The function countfrom is not run in a parallel thread. What happens here is that whenever the for-construct asks for the next value, the function will execute until it hits a yield statement. When the next value after that is required, the function resumes execution from where it left off.
And while you asked not to mention "generators", they are so intimately linked with yield that it doesn't really make sense to talk about the separately. What your countfrom function actually returns is a "generator object". It returns this object immediately after it is called, so the function body is not executed at all until something (e.g. a for-loop) requests values from the generator using its method .next().
the yield statement stores the value that you yield, until that function is called again.
so if you call that function (with an iterator) it will run the function another time and give you the value.
the point being that it knows where it left off last time
Python runs until it hits a yield and then stops and freezes execution. It's not continuing to run. It's hitting "after" on the next call to countfrom
It's easy to say that without making reference to generators but the fact is yield and generator are inextricably linked. To really understand it you've got to view them as the same topic.
It's easy to show yourself that what I (and others) have said is true by working with the generator from your example in a more manual way.
A function that yields instead of returning really returns a generator. You can then consume that generator by calling next. You are confused because your loop is taking care of all that in the background for you.
Here it is with the internals opened up:
def countfrom(n):
while n <= 12:
print "before yield, n = ", n
yield n
n += 1
print "after yield, n = ", n
your_generator = countfrom(10)
next(your_generator)
print "see the after yield hasn't shown up yet, it's stopped at the first yield"
next(your_generator)
print "now it woke back up and printed the after... and continued through the loop until it got to back to yield"
next(your_generator)
print "rinse and repeate"
Yield with and without for loop:
def f1():
print('f1')
yield 10
print(f'f1 done')
def generator_with_for_loop():
print(f'generator_with_for_loop')
for f1_gen in f1():
print(f'f1_gen={f1_gen}')
def generator_without_for_loop():
print(f'\ngenerator_without_for_loop')
gen = f1()
print(f'f1_gen={gen}')
print(gen.__next__())
try:
print(gen.__next__())
except StopIteration:
print('done')
if __name__ == '__main__':
generator_with_for_loop()
generator_without_for_loop()
"""
generator_with_for_loop
f1
f1_gen=10
f1 done
generator_without_for_loop
f1_gen=<generator object f1 at 0x7fd7201e54a0>
f1
10
f1 done
done
"""