when to use if vs elif in python - python

If I have a function with multiple conditional statements where every branch gets executed returns from the function. Should I use multiple if statements, or if/elif/else? For example, say I have a function:
def example(x):
if x > 0:
return 'positive'
if x < 0:
return 'negative'
return 'zero'
Is it better to write:
def example(x):
if x > 0:
return 'positive'
elif x < 0:
return 'negative'
else:
return 'zero'
Both have the same outcome, but is one more efficient or considered more idiomatic than the other?
Edit:
A couple of people have said that in the first example both if statements are always evaluated, which doesn't seem to be the case to me
for example if I run the code:
l = [1,2,3]
def test(a):
if a > 0:
return a
if a > 2:
l.append(4)
test(5)
l will still equal [1,2,3]

I'll expand out my comment to an answer.
In the case that all cases return, these are indeed equivalent. What becomes important in choosing between them is then what is more readable.
Your latter example uses the elif structure to explicitly state that the cases are mutually exclusive, rather than relying on the fact they are implicitly from the returns. This makes that information more obvious, and therefore the code easier to read, and less prone to errors.
Say, for example, someone decides there is another case:
def example(x):
if x > 0:
return 'positive'
if x == -15:
print("special case!")
if x < 0:
return 'negative'
return 'zero'
Suddenly, there is a potential bug if the user intended that case to be mutually exclusive (obviously, this doesn't make much sense given the example, but potentially could in a more realistic case). This ambiguity is removed if elifs are used and the behaviour is made visible to the person adding code at the level they are likely to be looking at when they add it.
If I were to come across your first code example, I would probably assume that the choice to use ifs rather than elifs implied the cases were not mutually exclusive, and so things like changing the value of x might be used to change which ifs execute (obviously in this case the intention is obvious and mutually exclusive, but again, we are talking about less obvious cases - and consistency is good, so even in a simple example when it is obvious, it's best to stick to one way).

Check this out to understand the difference:
>>> a = 2
>>> if a > 1: a = a+1
...
>>> if a > 2: a = a+1
...
>>> a
4
versus
>>> a = 2
>>> if a > 1: a = a+1
... elif a > 2: a = a+1
...
>>> a
3
The first case is equivalent to two distinct if's with empty else statements (or imagine else: pass); in the second case elif is part of the first if statement.

In some cases, elif is required for correct semantics. This is the case when the conditions are not mutually exclusive:
if x == 0: result = 0
elif y == 0: result = None
else: result = x / y
In some cases it is efficient because the interpreter doesn't need to check all conditions, which is the case in your example. If x is negative then why do you check the positive case? An elif in this case also makes code more readable as it clearly shows only a single branch will be executed.

In general (e.g. your example), you would always use an if..elif ladder to explicitly show the conditions are mutually-exclusive. It prevents ambiguity, bugs etc.
The only reason I can think of that you might ever not use elif and use if instead would be if the actions from the body of the preceding if statement (or previous elif statements) might have changed the condition so as to potentially make it no longer mutually exclusive. So it's no longer really a ladder, just separate concatenated if(..elif..else) blocks. (Leave an empty line between the separate blocks, for good style, and to prevent someone accidentally thinking it should have been elif and 'fixing' it)
Here's a contrived example, just to prove the point:
if total_cost>=10:
if give_shopper_a_random_discount():
print 'You have won a discount'
total_cost -= discount
candidate_prime = True
if total_cost<10:
print 'Spend more than $10 to enter our draw for a random discount'
You can see it's possible to hit both conditions, if the first if-block applies the discount, so then we also execute the second, which prints a message which would be confusing since our original total had been >=10.
An elif here would prevent that scenario.
But there could be other scenarios where we want the second block to run, even for that scenario.
if total_cost<10:
<some other action we should always take regardless of original undiscounted total_cost>

In regards to the edit portion of your question when you said:
"A couple of people have said that in the first example both if statements are always evaluated, which doesn't seem to be the case to me"
And then you provided this example:
l = [1,2,3]
def test(a):
if a > 0:
return a
if a > 2:
l.append(4)
test(5)
Yes indeed the list l will still equal [1,2,3] in this case, ONLY because you're RETURNING the result of running the block, because the return statement leads to exiting the function, which would result in the same thing if you used elif with the return statement.
Now try to use the print statement instead of the return one, you'll see that the 2nd if statement will execute just fine, and that 4 will indeed be appended to the list l using append.
Well.. now what if the first ifstatement changes the value of whatever is being evaluated in the 2nd if statement?
And yes that's another situation. For instance, say you have a variable x and you used if statement to evaluate a block of code that actually changed the x value.
Now, if you use another if statement that evaluates the same variable x will be wrong since you're considering x value to be the same as its initial one, while in fact it was changed after the first if was executed. Therefore your code will be wrong.
It happens pretty often, and sometimes you even want it explicitly to be changed. If that's how you want your code to behave, then yes you should use multiple if's which does the job well. Otherwise stick to elif.
In my example, the 1st if block is executed and changed the value of x, which lead to have the 2nd if evaluates a different x (since its value was changed).
That's where elif comes in handy to prevent such thing from happening, which is the primary benefit of using it.
The other secondary good benefit of using elif instead of multiple if's is to avoid confusion and better code readability.

Consider this For someone looking for a easy way:
>>> a = ['fb.com', 'tw.com', 'cat.com']
>>> for i in a:
... if 'fb' in i:
... pass
... if 'tw' in i:
... pass
... else:
... print(i)
output:
fb.com
cat.com
And
>>> a = ['fb.com', 'tw.com', 'cat.com']
>>> for i in a:
... if 'fb' in i:
... pass
... elif 'tw' in i:
... pass
... else:
... print(i)
Output:
cat.com
'If' checks for the first condition then searches for the elif or else, whereas using elif, after if, it goes on checking for all the elif condition and lastly going to else.

elif is a bit more efficient, and it's quite logical: with ifs the program has to evaluate each logical expression every time. In elifs though, it's not always so. However, in your example, this improvement would be very, very small, probably unnoticeable, as evaluating x > 0 is one of the cheapest operations.
When working with elifs it's also a good idea to think about the best order. Consider this example:
if (x-3)**3+(x+1)**2-6*x+4 > 0:
#do something 1
elif x < 0:
#do something 2
Here the program will have to evaluate the ugly expression every time! However, if we change the order:
if x < 0:
#do something 2
elif (x-3)**3+(x+1)**2-6*x+4 > 0:
#do something 1
Now the program will first check if x < 0 (cheap and simple) and only if it isn't, will it evaluate the more complicated expression (btw, this code doesn't make much sense, it's just a random example)
Also, what perreal said.

Related

Python - How to find which condition is true in if statement?

I have an if statement has many conditions, for example:
if(0 > 1 or 9 < 10 or 2 == 1):
print('Hello World!')
so i wanna know which is the right condition that let the if statement continues to print hello world? "without using another if statement or elif"
In my code I have lots of conditions so it's difficult to use a lot of else statements to just know what is the right condition was.
In general, it's impossible - each condition is evaluated, we can only get a result.
However, if instead of ors, we have them stored* in a list like this:
conditions = [0>1, 9<10, 2==1] # it gets evaluated here!*
if any(conditions):
print('Hello World!')
we could get the index of the True element by doing conditions.index(True).
[*] But be aware that conditions doesn't consist of pure conditions but of Trues and Falses because they got evaluated. That's why I said it's impossible to get the condition itself.
I don't know why you would ever want to use this but okay...
You need to return a value which has a special __bool__ so I would define a class.
The class will have one instance variable, index to indecate the first True condition, or None if there's no True condition.
The __bool__ function then only needs to check whether index is None:
class Any:
def __init__(self, *conditions):
self.index = None
for i, cond in enumerate(conditions):
if cond:
self.index = i
break
def __bool__(self):
return self.index is not None
Example usage:
if o := Any(0 > 1, 9 < 10, 2 == 1):
print(f'The index of the first True condition is {o.index}')
For hard coded conditions like in your example, a good IDE should have an indicator and propose that you simplify the condition.
If you have variables in the condition, this will of course not be possible. In such a case, I would refactor the code and introduce additional semantics by using a variable name for the individual boolean parts of the condition.
In PyCharm, the shortcut Ctrl+Alt+V extracts a condition into a variable.
A more realistic example (before):
class Customer:
def __init__(self, age, totalsales, paymenttype):
self.age = age
self.totalsales = totalsales
self.paymenttype = paymenttype
c = Customer(21, 3000, 2)
if c.age > 18 or c.totalsales > 5000 or c.paymenttype == 1:
print('Hello World!')
After:
c = Customer(21, 3000, 2)
is_adult = c.age > 18
is_vip = c.totalsales > 5000
is_payed_in_advance = c.paymenttype == 1
if is_adult or is_vip or is_payed_in_advance:
print('Hello World!')
When you reach the if-statement, you can inspect each part of the condition in the debugger.
Note that this may change the behavior of your program, because with the changed code, each condition is evaluated, whereas short circuiting may have been applied before. However, I never ran into a situation where this caused a problem.
Chained boolean expressions will be evaluated from left to right. If one of the chained statements is evaluated as being True, the remaining conditions will not be checked.
Assuming second_condition is fulfilled and hence will be evaluated as True, the following pseudo-code snipped would evaluate first_condition as False and then enter the if statement because of second_condition being True. However, third_condition will not be checked as another check before was already evaluated as True and thus the complete statement will become True:
if (first_condition or second_condition or third_condition):
pass
Knowing which condition was evaluated as True is not possible with the approach shown above. Therefore, I would suggest rewriting your checks as follows:
def handle_true(condition):
pass
if first_condition:
handle_true('first')
elif second_condition:
handle_true('second')
elif third_condition:
handle_true('third')
else:
pass
The if/elif will be evaluated in the same way as your chained or expression. If one condition fails, the next will be checked. If one of the given conditions is evaluated as True the branch will be entered. If none of the given conditions is fulfilled, the default else will be entered.
Combining this with the small helper function handle_true() should do the trick and not only provide a way to check which condition fired, but also provide a single place for handling any True condition.
I think that a good option will be to create a list of condition and to check you the item of your list in a loop.
cond=[True, False, True, 5>10,True,False,1==1,3<-1,'a' == 'a'] # assume this is your list
for i in range(len(cond)):
if cond[i]:
print(i) # will return the Item adress correspending to True
You can do:
print(0 > 1) print(9 < 10)
It will print true or false

Conditional that tests for a dictionary key's presence is always False

I made a function that consumes two dictionaries, curr_stats and weekly_result. If there are any keys in weekly_result that aren't in curr_stats, the function is supposed to just print invalid_msg, with no mutation of curr_stats.
But the if statement on the 5th line of my code doesn't seem to be working. It's supposed to trigger the next if statement, so no mutation of curr_stats occurs.
def update_standings(curr_stats, weekly_result):
invalid = 0
point_counter(weekly_result)
for team in weekly_result:
if team in curr_stats == False:
invalid = invalid + 1
if invalid > 0:
print(invalid_msg)
else:
for team in weekly_result:
curr_stats[team] = curr_stats[team] + weekly_result[team]
In Python, all comparisons have the same precedence, including in.
What's happening is comparison chaining, a special form intended to test transitive relationships like in math class:
if x_min < x < x_max:
...
As Paweł Kordowski pointed out in his comment, the above comparison chain is mostly equivalent to:
if x_min < x and x < x_max:
...
(There is one difference:
The "equivalent" code might evaluate x twice, while the comparison chain evaluates x exactly once.)
In your case, the comparison chain is:
if team in curr_stats == False:
...
...which is (mostly) equivalent to:
if team in curr_stats and curr_stats == False:
...
This is only true if curr_stats contains team and curr_stats is empty... which should never happen.
The problem with your code is the == False --- in part because it turned a comparison into a comparison chain, but mostly because you never needed it in the first place.
Python provides the not keyword for when you want a Boolean's opposite.
Your conditional statement should read:
if team not in curr_stats:
invalid = invalid + 1
One last suggestion:
This function can be made even shorter by getting rid of the invalid counter and just returning as soon as an invalid team is found.
(Once the you've discovered that weekly_result is invalid input, you probably don't care if it's "even more invalid".)
I also used dict.items to simplify the final for loop:
def update_standings(curr_stats, weekly_result):
point_counter(weekly_result)
for team in weekly_result:
if team not in curr_stats:
print(invalid_msg)
return
for team, result in weekly_result.items():
curr_stats[team] += result

PyCharm warns local variable might be referenced

Why does PyCharm highlight the boolean variable nearby the return with Local variable "boolean" might be referenced before assignment?
This code checks whether a number is prime or not:
import random
import math
import time
def prime_t(x):
print x
if x < 2:
return False
if x == 2:
return True
if x == 3:
return True
for i in range(2, int(math.sqrt(x))+1):
if x % i == 0:
boolean = False
break
else:
boolean = True
return boolean
random.seed()
how_much = input()
start = time.time()
for i in range(0, how_much):
print(prime_t(random.randint(0, 1000)))
print time.time()-start
I've read that might be some problem with global variables, but there's no ones which might be used in prime_t(). I had similar thing - exception while executing the code, but I think it has been eliminated with if x == 2 and if x == 3.
What else might be the problem?
PyCharm is not certain that boolean will be set. It is not smart enough to work out the flow of your code, so it doesn't know that your for loop will always have at least 1 iteration (since x > 3 is true by that point, provided x is an integer, of course).
Instead, it assumes that variables bound in a for loop could potentially never be set, and thus raises this warning.
The work-around is of course to set boolean = False before the loop, just to shut it up. It is only a warning, you could also just ignore it as the IDE is trying to help you but misunderstood.
Alternatively, instead of using a flag variable, you can use return False inside the for loop where you now use break, and return True at the end of your function:
def prime_t(x):
if x < 2:
return False
if x in {2, 3}:
return True
for i in range(2, int(math.sqrt(x))+1):
if x % i == 0:
return False
return True
For those looking to ignore this, put
# noinspection PyUnboundLocalVariable
Above the line.
Thanks to: https://github.com/whitews/pc-inspection-suppression-list/blob/master/suppress-inspection.csv
In general, code that's inside a for or while loop doesn't have to run. The condition for a while loop could be unmet as soon as the loop is reached initially. The for loop could be trying to iterate over something that's empty. If the loop doesn't run, and the code in the loop is the only place that a particular variable gets set, then it wouldn't get set. Trying to use it would cause an UnboundLocalError (a subtype of NameError) to be raised.
IDEs often try to detect this situation and offer warnings. Because they are warnings, they will be conservative. Python is a highly dynamic language and there's often very little that you can prove about the code's behaviour before it runs. So pretty well any loop that doesn't use constant, literal data (for x in [1, 2, 3]:) needs to be treated as "might not run at all".
And, indeed, if I try out the example function at the interpreter prompt, I can easily get that UnboundLocalError:
>>> prime_t(math.pi)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 14, in prime_t
UnboundLocalError: local variable 'boolean' referenced before assignment
After all, nothing else in the code type-checked the value; and pi isn't equal to 3, nor to 2, nor is it less than 2.
There are several reasonable approaches to the problem, in context.
We can simply assign boolean = True before the loop:
boolean = True
for i in range(2, int(math.sqrt(x))+1):
if x % i == 0:
boolean = False
break
return boolean
We can use an else clause on the loop to set the "default" value:
for i in range(2, int(math.sqrt(x))+1):
if x % i == 0:
boolean = False
break
else:
boolean = True
return boolean
The else block code runs whenever there isn't a break out of the loop; in particular, it runs if the loop doesn't run (since there was nothing to break out of). So static checking tools should be able to verify that our bases are covered. Many people don't like this syntax because it's confusing and often unintended (it looks like a typo, right?). However, this use pattern is pretty much exactly why it's in the language. (That said, I think we can do better; keep reading.)
Since there is nothing more to do after this loop in the function, we don't need a flag at all; we can return False as soon as we find a factor, and return True at the end of the function (since we didn't find one if we got this far):
for i in range(2, int(math.sqrt(x))+1):
if x % i == 0:
return False
return True
(Notice that in all of these cases, I removed the else from the if - because it doesn't serve a purpose here. It doesn't make a lot of logical sense to keep reminding ourselves that we didn't find a factor yet - we wouldn't still be looping if we did.)
All of that said, for "searching" loops like these I prefer to avoid explicit for loops altogether. The built-in any and all functions are practically designed for the purpose - especially when paired with a generator expression, which ensures their short-circuiting behaviour stays relevant:
return not any(x % i == 0 for i in range(2, int(math.sqrt(x))+1))
Equivalently (following de Morgan's law):
return all(x % i != 0 for i in range(2, int(math.sqrt(x))+1))

Why is my condition expression giving a syntax error?

def singleNumber(nums):
for num in set(nums):
return num if nums.count(num) != 2 #error occurs here
print(singleNumber([1,1,4,5,5]))
This follows the usual python condition expression format. I don't understand why it's giving me a syntax error here.
The purpose of this function is to find number that doesn't occur twice.
If you're going to actually execute a return, you must return something no matter what.
So you could try:
return num if nums.count(num) != 2 else None
However, that's not going to work in this case since it will return on the first check rather checking all the elements for what you want.
In other words, let's say the first element checked is the first 1 in [1,1,4,5,5]. It will work out that there are two copies of that value in the array and then return None, skipping the rest of the elements.
I'd probably rewrite it as:
if nums.count(num) != 2: return num
which basically does what you need in that, if you don't explicitly return something (i.e., all of the values occur twice), the caller gets None implicitly when the function exits.
If you're the type that dislikes implicit things, you can explicitly add return None at the end of the function, though it's not really necessary.
Because you forgot to write what the result should be if the condition is false.
return num if nums.count(num) != 2 else ...what?
And if you didn't want that then you should write a normal if statement instead.
When you use a ternary conditional operator both if and else part are mandatory.
return num if nums.count(num) != 2 else <missing???>

Why does python use 'else' after for and while loops?

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.

Categories