If we have more than one if statement, is it possible to combine them? Like this:

x = raw_input("> ")
if x == a:
    print "1a"
elif x == b:
    y = raw_input("> ")
    if y == a:
        print "1y"
    elif y == b:
        print "1b"
    elif y == c:
        print "1c"
        xy = raw_input("> ")
        if xy = a:
            print "x2"
        elif xy = b:
            print "x1"
        else:
            print "x1b"
    else:
        print "1x"
elif x == c:
    n = 1+2
    print n
elif x == d:
    print "1d"
else:
    print "1y"

Or otherwise something similar.

Recommended Answers

All 12 Replies

Certainly. Nested if:s are a common thing, though you generally don't want the nesting to go too deep for the sake of readability.

How do we combine them?

The more pythonic way to do multiple if/elif/elif/elif/.../else branching is to have a dictionary that maps the value of the tested variables to a function.

# case statement in python
def a(): pass
def b(): pass
def default_func(): pass
dispatcher{'a':a,'b':b}
# mimic if 'a' elif 'b' else
if key not in dispatcher:
  default_func()
else:
  dispatcher[key]()

The various functions you call can of course ask for further input which can be used for further dispatch

Erm... I do not understand you, sir. Can you make it more easier to understand to a newb?

I think you are trying to understand how to best code to consider a complex set of logical conditions. There are two ways to code such things:

  1. You can do it as you showed with nested if/elif/else statements. This has the advantage of being straight forward, but the disadvantage of being hard to get entirely into your mind. It looks the same as a logic tree in nearly any language.
  2. You can do it by careful pre-planning. This works only if you already have all the logical conditions "in place" (it will not work if you ask the user only sometimes for more input). It has the advantage of being a little easier to see 'the big picture', but is not quite so straight forward. This is the equivalent of a 'case' or 'switch' statement in other languages.

Lets do an example: Suppose you have something like this: The user must choose a shape, a color and a size. You want to do something different depending on those choices. Option one would look like this:

if 'triangle' == shape:
  if 'red' == color:
     if 'small' == size:
       do_small_red_triangle()
     elif 'medium' == size:
       do_medium_red_triangle()
     elif 'large' == size:
       do_large_red_triangle()
     else:
       raise SizeException(size)
  elif 'blue' == color:
    # all the same size options
  elif 'yellow' == color:
    # all the same size options
  else:
    raise ColorException(color)
elif 'circle' == shape:
  # all the same size and color options
elif 'square' == shape:
  # all the same size and color options
else:
  raise ShapeException(shape)
#
def do_small_red_triangle():
  print("Small Red Triangle")
# and dozens more 'do_"* functions

This is pretty straight forward, but it is boring to write the code and the probability that you will make a mistake is not low. You can give good error messages because everything is all spread apart. The logic tree takes about 85 lines to write and that plus the do_* functions and three Error classes are all there is.

The other way looks like this:

def do_small_red_triangle():
  print("Small Red Triangle")
def do_medium_red_triangle():
  print("Medium Red Triangle")
# and 25 other functions with obvious names
dispatcher = { 
  ('small','red','triangle'):do_small_red_triangle,
  ('medium','red','triangle'):do_medium_red_triangle,
  # 24 more entries followed by the last:
  ('large','yellow','square'):do_large_yellow_square,
}

if not (size,color,shape) in dispatcher: # illegal choice of some kind
  raise BadOptionException(size,color,shape)
else:
  # look up the function and call it
  dispatcher[(size,color,shape)]()

This second way is a little bit shorter than the first way because it only takes 27 lines to define dispatcher, and 4 lines to use it compared to 85 lines for the logic tree. It may run faster because looking something up in a dictionary is a built-in operation, but logic has to be done at run time. It is probably a little easier to understand because the dispatch is all done in one spot, so you can see what is intended without scanning many lines of code, and because you are filling out the options all in one place you are also less likely to forget one, reverse your logic, or otherwise screw up. However, your error message is more generic now because we only have the tuple that is either there or not. A good error message would add some code (but all in the error condition code: Nicely all in one place).

Option 2 is the 'more Pythonic' way to do a switch statement, works very well for large sets of options, but is a little long winded for a small set of options. Option 1 is perfectly legal and probably easier to understand for small sets of options, but is not so nice for large option sets.

The dictionary dispatch technique (option 2) has some very slightly tricky bits: If there are some combinations that should not be handled, you still put them in the dictionary, but you make the value for that key be None, and the dispatch check looks like

if (shape,size,color) in dispatcher and dispatcher[(shape,size,color):
  dispatcher[(shape,size,color)]()
else:
  raise BadOptionException(shape,size,color)

You can also re-use the callback functions if that makes sense, just as you could with the logic tree.

Hope this is more helpful. Enjoy.

I see. So the dispatcher are the choices?

Then how about implementing inputs and something like 4+6 or 10+2?

If you need to ask the user for input depending on prior options, then you cannot fully use the dispatcher technique. It only works if

  1. You already have all the conditions (which you do not if some condition is based on user input after you dispatch based on other conditions)
  2. You know all the possible values for each condition variable (which you do not if the condition is open ended, such as Starts with 'a' or greater than 10)

You can sometimes work around the second issue by making a 'dummy' condition variable: is_a = user_input.startswith('a') or gt_10 = int(user_input) > 10 then the dispatch tuple is either True or False for that condition.

If you are implementing a calculator, the dispatcher technique is probably not best. I would probably write a state machine to parse the user input, based on the usual binding rules such as a + b * c == a + (b * c) You change states based on which operator you see next, and parentheses (hint: It is easier if you require reverse polish notation because parentheses are not needed for that)

I see. So the dispatcher are the choices?

Then how about implementing inputs and something like 4+6 or 10+2?

Not allowed to use eval?

Not allowed to use eval?

Hi Tony. You can use eval if you trust the input to be non-malicious and correct. (i.e. Not to do something horrible). I went through three iterations of this myself:

  1. Ignorance (I did it the hard way)
  2. eval (which felt like enlightenment at the time)
  3. Enlightenment (I would do it a better hard way)

I never actually wrote option 3

Little check of input can help things, like in my calculator snippet:

def ev(s):
    if '__' in s: return 'Safety Error'
    ## try to fix bracket unbalance 'sin(pi' for example
    brackc = s.count('(') - s.count(')')
    try:    return eval((s + ')' * brackc).replace(',','.')) ## decimal point not comma
    except: return 'Error'

http://www.daniweb.com/forums/post1235849.html#post1235849

If you want to restrict eval more, and do only basic calculations you can do like this:

>>> valid = set(string.digits) | set('+-*/. ()')
>>> expr = '123.3242 / 3*4+2/(3.0**3/2)'
>>> if all(c in valid for c in expr): print eval(expr)

164.580414815
>>> expr = '123.3242 / 3*(4+2/3.0)**3/2'
>>> if all(c in valid for c in expr): print eval(expr)

2088.89879506
>>>

Indeed, that appears to be a better "hard" way. Thank you for the enlightenment.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.