I have a for loop in python like this:
for item in items:
onwards = make_flow_decision(item)
if onwards == 'break':
break
elif onwards == 'continue':
continue
elif onwards == 'carry_on':
process_the_item(item)
So idea is that flow control for the loop is factored out because it is really complex.
Is there a better way of doing this than passing strings around and checking for special strings like 'continue', 'break', and 'carry_on'?
Looking for ideas generally that make this a little more elegant.
2 Answers 2
What you could do, is factor out the filtering and breaking into a generator, which can be really handy if that logic is used multiple times, with different processing in the middle.
def relevant_items(items):
for item in items:
# logic to continue or break/return goes here
yield item
def process_many_items(items):
for item in relevant_items(items):
process_item(item)
This does however add an extra layer of indirection, which is probably only worth it if the relevant_items method would make sense outside process_many_items.
-
Indeed,
relevant_items
does make more sense outside of the processing loop. I dig this approach. Will give it a go.mlissner– mlissner2015年08月11日 14:26:41 +00:00Commented Aug 11, 2015 at 14:26 -
Actually, I take it back! Darn. This won't work because some processing of the item has to happen before the continue/break/return decision can be made. So the loop is more like, for item in items, calculate some stuff, using that stuff, make a decision, using that decision, continue/break/carry_on.mlissner– mlissner2015年08月11日 14:28:51 +00:00Commented Aug 11, 2015 at 14:28
You can use enumerated types instead of strings, and keep your implementation.
Or, if you don't mind using exceptions you can write:
try:
for item in items:
if shall_process(item):
process_the_item(item)
except StopIteration:
pass
Here, shall_process
returns a boolean and can throw an exception to exit the iteration.
-
Not a bad approach. The logic feels a little oblique though. Like, it's much easier to understand the flow control of my first example than it is to figure out that
shall_process
is emulating abreak
by throwing an exception. The one thing that might help here though is theStopIteration
exception. The enums approach seems a little less confusing though, I'll say that.mlissner– mlissner2015年08月08日 23:45:26 +00:00Commented Aug 8, 2015 at 23:45
continue
cases can befilter()
ed out and the "normal" case can bemap()
ed, but thebreak
may be tricky depending on how much you care about leaving some but not all items processed at the end.make_flow_decision()
was cheap to check once a break was needed. Then, instead of actuallybreak
ing out of the loop, I could just continue through it until it's done. It'd mean needless iteration, but if cheap enough that's not so bad. The resulting code could be simplified to,if press_on: process_the_item()
. And the check would just emulate a break by continuing until the last item.