strange while statement behaviour? - python

I cannot figure out why the following statements dont work.
randomKey = random.choice(list(topic.keys()))
randomValue = random.choice(topic[randomKey])
current = "-" * len(randomValue)
while current != randomValue:
(statements)
else:
(statements)
However, if i alter the 1st line to
while current == randomValue:
the statement after 'else' executes correctly. Otherwise, the statement after 'else' does not execute. Any idea why what may be causing the strange behaviour? Full code has been excluded for it will run through this entire page.

It is part of the Python grammar. From the documentation:
This [a while statement] 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.
So in the first case, it must be that the while condition never evaluates to false, while in the second it eventually does. Note that explicitly breaking out of the loop will not execute the else clause.

else, when used with while, runs after the while expression evaluates to a falsy value if the while loop finishes by the expression being false, instead of being broken out of by a break statement (or execution leaving the function via a return or raise-ing an exception). Your while condition in your second example must fail, so there's no opportunity for a break to occur, the function to return or an exception to be thrown, so the else statements will always run.
docs for while

Related

Does continue prevent the else block from running in a for/else statement?

In a for loop with break statements I can add an else statement at the end, which will be triggered if my for loop never hits a break statement.
My question is, how does continue affect this?
continue does not affect the else: clause. The else clause is run if the loop terminated normally, that is, if a StopIteration is (implicitly) raised by the iterator.
The continue statement does nothing for the particular iteration, however it does not prevent the iterator from being exhausted.
No, else clause execution is not affected by continue.
The only thing that prevents an else statement (after a for loop) from being triggered is a break statement (or your code returning or exiting or raising an exception before it finishes the for loop).
for i in range(5):
continue
else:
print("else triggered")
Will print else triggered.
See the docs:
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
[...]
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.
Only break is mentioned as preventing the else clause from running, continue isn't.

Python if, elif, else does not run if, elif, or else statement

I'm using an if, elif, else statement in a method. When I run the code it doesn't run the if, elif, or else, but when I comment out the elif statement it defaults to the else as expected.
def message(response):
args.pop(0)
if com_text == "trivia":
# triviaActive will be False
if triviaActive:
# Performs action
return True
# args[0].lower() is not equal to 'start'
elif args[0].lower() == "start" and not triviaActive:
# Performs action
return True
# So it should defaults to this else
else:
# Performs action
return True
I added some comments above the chain of if, elif, and else statements that's giving me the unexpected results. Instead of it defaulting to the else statement, as it should in this situation, it just returns from the method without running the rest of the method. I even tried using print statements inside of the if, elif and else statements, but the program doesn't run them.
(edit)
I simplified my if, elif, and else. Even with this simplified version I still have a problem with my elif. Also I want to mention that I use the pop method to pop a value off of args and makes it empty which could be the issue.
Test the code by replacing
com_text == "trivia":
With
True:
None of your conditions will run if that does not hold.
If everything seems fine after that change, then it could be that your editor does not like your blank line spacing. As a sanity check, remove the blank lines and see if it works as expected (some editors may not work if the blank line does not have proper indentation, and you seem to have no indentation on your blank lines).

if, elif and else.. priority and chains

I always had this curiosity about Python and I can't find a clear answer, maybe someone can help me
what is the "if", "elif" and "else" priority and way of working?
if I do:
if conditionA:
do something
elif conditionB:
do something else
else:
do something else
does the "else" check the condition in "elif" or both conditions "if" and "elif"?
is there any interesting order in which they can be used (e.g. if, else, elif, else etc.) ?
thanks
else is the catch-all after all the if and elif statements were false. Putting it in the middle would be a syntax error.
Don't think of the else "checking the conditions." Think of it as the control flow never even reaches the else if the others were true. The same is true for multiple elif.
Consider this:
if False:
print 'first'
elif True:
print 'second'
elif True:
print 'third'
Even third won't print, because the second one was true. It doesn't matter that the third was true, because it's not even evaluated.
This can have some side effects too, if you're calling functions:
if send_email(to_coworker):
pass
elif send_email(to_boss):
pass
Assuming the function returns True if it succeeds, the email to your boss will only send if the email to your coworker fails, even though the function would need to be called to evaluate the elif condition
They're processed in the order that they're encountered in the file.
if True:
# Always executes.
elif False:
# Is never checked.
else:
# We never get this far, either.
As soon as the first condition that evaluates True is encountered the corresponding block of code is executed. Additional conditions aren't checked.
They don't have priorities for itself, what really matters is the order, so the first condition will be checked and if the first condition is False than the second condition is checked and so on until any condition is True or it executes the else statement if and only if all conditions where False
In your example if conditionA is True then the code inside the if statement will be executed and the elif and else conditions will not matter at all.
If conditionA is False and conditionB is True than the code inside the elif statement will be executed.
Finally only when the conditionA and conditionB is False then the code inside else block will be executed

Flow control after breaking out of while loop in Python

I am pretty new to both programming and Python. A few times now, I have created what feels like an awkward program flow, and I am wondering if I am following best practices. This is conceptually what I have wanted to do:
def pseudocode():
while some condition is true:
do some stuff
if a condition is met:
break out of the while loop
now do a thing once, but only if you never broke out of the loop above
What I've ended up doing works, but feels off somehow:
def pseudocode():
while some condition is true:
do some stuff
if some condition is met:
some_condition_met = True
break out of the while loop
if some_condition_met is False:
do a thing
Is there a better way?
You're looking for while-else loop:
def pseudocode():
while some condition is true:
do some stuff
if a condition is met:
break out of the while loop
else:
now do a thing once, but only if you never broke out of the loop above
From docs:
while_stmt ::= "while" expression ":" suite
["else" ":" suite]
A break statement executed in the first suite terminates the loop
without executing the else clause’s suite.
Use an else clause to the while loop:
while some_condition:
do_stuff()
if a_condition_is_met:
break
else:
executed_when_never_broken
See the while statement documentation:
A break statement executed in the first suite terminates the loop without executing the else clause’s suite.
If you think about it, you might already have a perfectly good condition to use without setting a flag: the condition at the top of the while loop (or, rather, the not of it). Assuming you don't change the truthiness of your condition during the same iteration as a break, this gives you:
while something:
do_stuff()
if something_else:
break
if not something:
more_stuff()
This makes sense if you think of while as a repeated if: instead of happening once, a while keeps going until the condition becomes falsey.
But, like the other answers have mentioned, the analogy goes further: just like you don't have to spell all your ifs as
if a:
a_stuff()
if not a:
b_stuff()
while accepts an else that is executed if the condition at the top is tested and found to be falsey. So,
while something:
do_stuff()
if something_else:
break
else:
more_stuff()
And, same as the if/else case, this doesn't imply any further tests of the condition than what would happen anyway. In the same way that an else attached to if a won't run if the true-branch makes a falsey, an else attached to a while will never run after a break since that explicitly skips ever checking the condition again. This makes the else: equivalent to a decicated flag in every case.
This also extends to for loops, even though the analogy breaks - but it executes under similar enough rules: the else is run if the loop ends by having run its course rather than hitting a break.

Why is os.path.basename() making my HTPC script fail?

Full script here: http://pastebin.com/d6isrghF
I'll admit I'm very new to Python so please forgive my stupidity if this is an easy question to answer. The section in question is this:
sourcePath = jobPath
while os.path.basename(sourcePath):
if os.path.basename(os.path.dirname(sourcePath)).lower() == category.lower():
break
else:
sourcePath = os.path.dirname(sourcePath)
if not os.path.basename(sourcePath):
print "Error: The download path couldn't be properly determined"
sys.exit()
jobPath is being fed to the script from sabnzbd and is:
/mnt/cache/.apps/sabnzbd/complete/name.of.folder
category is:
tv
So I guess my question is: why is this failing with the error?
Why it does not work
Your code cannot work because while is executed until os.path.basename(sourcePath) is not evaluated to True, then if statement is called, which (because it looks like: if not os.path.basename(sourcePath)) is obviously evaluated as True and thus the message (your "error") is shown:
Annotated source code
sourcePath = jobPath
# This is executed until os.path.basename(sourcePath) is evaluated as true-ish:
while os.path.basename(sourcePath):
if os.path.basename(os.path.dirname(sourcePath)).lower() == category.lower():
break
else:
sourcePath = os.path.dirname(sourcePath)
# Then script skips to the remaining part, because os.path.basename(sourcePath)
# has been evaluated as false-ish (see above)
# And then it checks, whether os.path.basename(sourcePath) is false-ish (it is!)
if not os.path.basename(sourcePath):
print "Error: The download path couldn't be properly determined"
sys.exit()
When (and why) it sometimes works
It sometimes works only because category is found in the path, which means while loop will be exited (using break) even despite still meeting criteria (the condition after while keyword: os.path.basename(sourcePath)). Because the condition from while loop is still met (we exited the loop even though it was met), the next statement's condition (not os.path.basename(sourcePath)) is no longer met and the message ("the error") is not printed.
Possible solutions
I believe one of the solutions is to add a counter to your code, that will print the error only if in specific number of iterations you will not be able to find what you needed. You can also try to catch "too many recursions" exception (if you will use recursion, of course, but the error will be like this: RuntimeError: maximum recursion depth exceeded).
However, you should redesign it rather to meet your own needs.

Categories

Resources