Reputation: 307
Consider the following situation:
flag = 'a'
while True:
try:
# something that might fail
except:
# fix failure
flag = 'b'
else:
break
print(flag)
I feel like there must be a better way to find out what happened without using flags in this case, but can't think of anything that's more "pythonic".
Upvotes: 1
Views: 185
Reputation: 5871
I think this is more of a general programming question. Use good names that mean what you mean. What does "flag" mean? Rename it to "done" and reread the code, and you see a bunch of unnecessary stuff for a simple while loop.
numerator = 1
denominator = 0
done = False
while not done:
try:
x = numerator // denominator
done = True
except:
denominator = 1
But to have a good accounting of what happened, and why, we want to add logging (also general programming, not specifically pythonic). This will allow you to keep track of many different code paths in many different exception handlers, for example.
import logging
# one time setup, can be in a different file
log = logging.getLogger('myapp')
log.setLevel(logging.ERROR)
fh = logging.FileHandler('myapp.log')
log.addHandler(fh)
log.info('about to begin')
done = False
while not done:
try:
x = n // d
done = True
except NameError as e:
log.error('oops forgot to initialize: ' + str(e))
n = 1
d = 0
except ZeroDivisionError as e:
log.error('division by zero')
d = 1
except Exception as e:
log.error('unknown error: ' + str(e))
log.info('done')
The errors will be stored in the log file you specified. You have a lot of control over whether or not it logs, and where it puts the logs. For example, I have log.info messages for begin and end, but you don't always want everything. They do not log because I set the log level to ERROR, so this log file 'myapp.log' will only have the log.error messages.
Think of logging like a print statement that only goes where you want it to go, if you actually want to know about it, so you can efficiently leave it in there. logging.DEBUG messages are especially good for knowing when stuff happened in the program as you debug it, but are disabled later by setting a different logging level.
Upvotes: 2
Reputation: 5682
There is another pattern that I like:
while <failing>
Example:
# It only works for positive numbers
In [24]: def run_safe(x):
...: if x < 0:
...: x = 0
...: try:
...: return 100 / x
...: except Exception:
...: return None
In [25]: i = -4
In [26]: while run_safe(i) is None:
...: print("i={} failed".format(i))
...: i += 1
...:
...:
i=-4 failed
i=-3 failed
i=-2 failed
i=-1 failed
i=0 failed
# At the end i has the value that succeeds
In [27]: print(i)
1
For me, this method separates the risky code (which lives in the function now) from the "fix" which lives in the while loop. I find this a lot easier to read when either of the codes (risky or fix) are complex.
Upvotes: 1
Reputation: 10306
You could pull the handling for the exception / default cases into the except
and else
blocks instead. This is different code, however, because now you'll be printing on every exception instead of only once, after exiting the while
loop, so this would make more sense for a try
/except
/else
outside of a loop. If you're inside the loop but still only want to print once when the loop is done (i.e. once no exception is raised), I think your method is clear enough.
while True:
try:
# something that might fail
except:
# fix failure
print("b")
else:
print("a")
break
Upvotes: 2