I am looking for an elegant way to write this code. (Python 2.7)

The code below sits in a for loop. When the starting status of an item is say, s2, I need it to run iteratively until it reaches status s7.

If the success returns False, then it will stop and stay at that level, and the loop continues with the next item.

I'm hoping not to use try: to avoid the use of exceptions. The True / False success boolean should be sufficient for this purpose. Please correct me if you think I should consider otherwise.

Here's the code I have attempted. I am trying to find a more elegant solution.

def do_s1(): # similar for other status
  if condition:
    success = True
  else:
    success = False
  return success

for i in items:
  if status[i] = s1:
    success_s1 = do_s1()
    if success_s1:
      success_s2 = do_s2() 
      if success_s2:
        success_s3 = do_s3()
        # all the way until do_s7(), iteratively

  if status[i] = s2:
    do_s2()
    if success:
      do_s3() # all the way until do_s7(), iteratively
...

  if status[i] = s7:
    do_s7()
    # some code

The loop should iterate to the next item every time success turns False, or when it reaches s7

3 Answers

0
GiodoAldeima On

You can use the ternary operator in the do_s1 function:

return True if condition else False

The remaining code is a bit confusing since I don't know what type of variables status contains and what s1,s2 and s3 are.

1
chepner On

Normally, I don't recommend using list comprehensions or generators solely for the side effect of executing a function on each item of a list. Here, however, it has the potential to greatly reduce the size of your code.

Start by putting each of your do_* functions in a list, and each status in a separate list:

dos = [do_s1, do_s2, do_s3, do_s4, do_s5, do_s6, do_s7]
ss = [s1, s2, s3, s4, s5, s6, s7]

Now, we'll take advantage of the all function, which consumes elements of a sequence until it finds one that is false

for i in items:
    for j, s in enumerate(ss):
        if status[i] == s:
            all(f() for f in dos[j:])

And that's it. all returns True if all the calls return True, but you aren't actually interested in the return value of all, just the fact that it stops evaluating functions as soon as one of them returns False.

You could, however, replace the call to all with an third explicit loop:

for f in dos[j:]:
   if not f():
       break

all works here because your nested if statement can be simplified from

  if status[i] == s1:
    success_s1 = do_s1()
    if success_s1:
      success_s2 = do_s2() 
      if success_s2:
        success_s3 = do_s3()
        # all the way until do_s7(), iteratively

to

if status[i] == s1:
    if do_s1() and do_s2() and ... do_s7():

with and short-circuiting the evaluation of the various do_* functions whenever one fails.

0
Jdizzle On

I second Giodo's recommendation to use the ternary operator.

You could try to end the do_s1() function with a call to the next one on a successful execution. Then you wouldn't have to do the hierarchical calls explicitly:

def do_s1(): # similar for other statuses
  success = True if condition else False
  if success:
    do_s2()

def do_s2():
  success = True if condition else False
  if success:
    do_s3()

...

if status[i] == s1:
  do_s1() # goes all the way up to s7

and if you get other statuses that start in between, the sequential execution still works:

if status[i] == s2:
  do_s2() # also goes until s7