[Tutor] A Better Way to Write This?

Tim Peters tim.one@home.com
Sat, 17 Feb 2001 22:18:51 -0500


[Britt Green]
> I have a tiny program that asks a user to enter a color choice. There are
> four valid choices for the user to enter. I have a function that
> checks to  see if the user entered one of the valid choices. A snippet
> from it looks like this:
>
>     if choice == ("black") or choice == ("white") or \
>        choice == ("green") or choice == ("blue"):
>         return 1
>     else:
>         return 0
>
> While this works, it seems ugly.

Here are two easy steps:

1. The parentheses aren't needed, so:

     if choice == "black" or choice == "white" or \
        choice == "green" or choice == "blue":
         return 1
     else:
         return 0

2. What value does choice == "black" yield?  Well, 1 if it's black, 0 if
it's not.  Same for the rest of them.  So this does the same thing:

    return choice == "black" or choice == "white" or \
           choice == "green" or choice == "blue"

That is, the if/else isn't needed either.

> What would happen, for example, if there were ten or twenty
> correct colors?

It would get *very* ugly <wink>.  And what if there were hundreds?
Thousands?

> So I'm wondering if there is a better way to write this?

There are three ways you're more likely to see this written, two using lists
or tuples, and one using dicts.

1.

_VALID_COLORS = ["black", "white", "green", "blue"]

def is_valid_color(choice):
    for valid in _VALID_COLORS:
        if choice == valid:
            return 1
    return 0

This scales to any number of colors, of course.  If there are many of them,
it's helpful to sort the list of colors, most popular color first.  That way
you'll usually get out of the loop more quickly.

2. Actually the same as #1:

def is_valid_color(choice):
    return choice in _VALID_COLORS

People who come to Python from languages like C are likely to use #1, not
realizing that Python has the higher-level "in" operator that searches over
a list (or tuple, or any other sequence) for them.  #2 is easy to recommend,
but you can do a lot better in *speed* if there are thousands, or hundreds
of thousands(! it can happen) of "good" choices.  Like so:

3.

_VALID_COLOR_DICT = {}
for choice in _VALID_COLORS:
    _VALID_COLOR_DICT[choice] = 1  # value irrelevant

def is_valid_color(choice):
    return _VALID_COLOR_DICT.has_key(choice)

Unlike searching for something in a list, dict lookup goes very fast even if
the number of good choices is huge.  dicts are way cool.  But they take a
little more effort to set up.

Just for fun, here's a reasonably quick method you'll almost never see
<wink> (note that this uses string methods, which were new in Python 2.0):

4.

_VALID_COLORS = " black white green blue "

def is_valid_color(choice):
   return _VALID_COLORS.find(" " + choice + " ") >= 0

So, lots of choices.  If you want to learn only one, learn #3, because it
remains quick and just as easy no matter how large the set of choices grows.