How to handle both integer and string from raw_inp

2019-02-18 15:53发布

问题:

Still trying to understand python. It is so different than php.

I set the choice to integer, the problem is on my menu I need to use letters as well.

How can I use integer and string together?
Why can I not set to string than integer?

def main(): # Display the main menu
    while True:
        print
        print "  Draw a Shape"
        print "  ============"
        print
        print "  1 - Draw a triangle"
        print "  2 - Draw a square"
        print "  3 - Draw a rectangle"
        print "  4 - Draw a pentagon"
        print "  5 - Draw a hexagon"
        print "  6 - Draw an octagon"
        print "  7 - Draw a circle"
        print
        print "  D - Display what was drawn"
        print "  X - Exit"
        print

        choice = raw_input('  Enter your choice: ')

        if (choice == 'x') or (choice == 'X'):
            break

        elif (choice == 'd') or (choice == 'D'):
            log.show_log()

        try:
            choice = int(choice)
            if (1 <= choice <= 7):

                my_shape_num = h_m.how_many()
                if ( my_shape_num is None): 
                    continue

                # draw in the middle of screen if there is 1 shape to draw
                if (my_shape_num == 1):
                    d_s.start_point(0, 0)
                else:
                    d_s.start_point()
                #
                if choice == 1: 
                    d_s.draw_triangle(my_shape_num) 
                elif choice == 2: 
                    d_s.draw_square(my_shape_num) 
                elif choice == 3:             
                    d_s.draw_rectangle(my_shape_num) 
                elif choice == 4:             
                    d_s.draw_pentagon(my_shape_num) 
                elif choice == 5:             
                    d_s.draw_hexagon(my_shape_num) 
                elif choice == 6:             
                    d_s.draw_octagon(my_shape_num) 
                elif choice == 7: 
                    d_s.draw_circle(my_shape_num)

                d_s.t.end_fill() # shape fill color --draw_shape.py-- def start_point

            else:
                print
                print '  Number must be from 1 to 7!'
                print

        except ValueError:
            print
            print '  Try again'
            print

回答1:

Let me answer your question with another question:
Is it really necessary to mix letters and numbers?
Can't they just be all strings?

Well, let's take the long way and see what the program is doing:

  1. Display the main menu
  2. Ask/receive the user input
    • If is valid: ok
    • If not: print an error message and repeat
  3. Now we have a valid input
    • If is a letter: do a special tasks
    • If is a number: call the right draw-function

Point 1. Let's make a function for this:

def display_menu():
    menu_text = """\
  Draw a Shape
  ============

  1 - Draw a triangle
  2 - Draw a square
  D - Display what was drawn
  X - Exit"""
    print menu_text

display_menu is very simple so there's no need to explain what it does, but we'll see later the advantage of putting this code into a separate function.

Point 2. This will be done with a loop:

options = ['1', '2', 'D', 'X']

while 1:
    choice = raw_input('  Enter your choice: ')
    if choice in options:
        break
    else:
        print 'Try Again!'

Point 3. Well, after a second thought maybe the special tasks are not so special, so let's put them too into a function:

def exit():
    """Exit"""  # this is a docstring we'll use it later
    return 0

def display_drawn():
    """Display what was drawn"""
    print 'display what was drawn'

def draw_triangle():
    """Draw a triangle"""
    print 'triangle'

def draw_square():
    """Draw a square"""
    print 'square'

Now let's put it all together:

def main():
    options = {'1': draw_triangle,
               '2': draw_square,
               'D': display_drawn,
               'X': exit}

    display_menu()
    while 1:
        choice = raw_input('  Enter your choice: ').upper()
        if choice in options:
            break
        else:
            print 'Try Again!'

    action = options[choice]   # here we get the right function
    action()     # here we call that function

The key to your switch lies in options that now is no more a list but a dict, so if you simply iterate on it like if choice in options your iteration is on the keys:['1', '2', 'D', 'X'], but if you do options['X'] you get the exit function (isn't that wonderful!).

Now let's improve again, because maintaining the main menu message and the options dictionary it's not too good, a year from now I may forget to change one or the other, I will not get what I want and I'm lazy and I don't want to do twice the same thing, etc...
So why don't pass the options dictionary to display_manu and let display_menu do all the work using the doc-strings in __doc__ to generate the menu:

def display_menu(opt):
    header = """\
  Draw a Shape
  ============

"""
    menu = '\n'.join('{} - {}'.format(k,func.__doc__) for k,func in opt.items())
    print header + menu

We'll need OrderedDict instead of dict for options, because OrderedDict as the name suggests keep the order of its items (take a look at the official doc). So we have:

def main():
    options = OrderedDict((('1', draw_triangle),
                           ('2', draw_square),
                           ('D', display_drawn),
                           ('X', exit)))

    display_menu(options)
    while 1:
        choice = raw_input('  Enter your choice: ').upper()
        if choice in options:
            break
        else:
            print 'Try Again!'

    action = options[choice]
    action()

Beware that you have to design your actions so that they all have the same signature (they should be like that anyway, they are all actions!). You may want to use callables as actions: instances of class with __call__ implemented. Creating a base Action class and inherit from it will be perfect here.



回答2:

I'm not entirely clear what you're asking here. raw_input() always returns a str type. If you want to convert user input automatically to an int or other (simple) type you can use the input() function, which does this.

You have chosen to let the user enter either a letter or a number, you could equally assign the "letter" options to number in the menu. Or, you could take more advantage of the try/except, e.g.:

try:
    choice = int(user_input)
    if choice == 1:
        # do something
    elif ...
except ValueError: # type(user_input) != int
    if choice == 'X' or choice == 'x':
        # do something
    elif ...
    else:
        print 'no idea what you want' # or print menu again


回答3:

I believe your code can be simplified a bit:

def main():
    while 1:
        print
        print "  Draw a Shape"
        print "  ============"
        print
        print "  1 - Draw a triangle"
        print "  2 - Draw a square"
        print "  3 - Draw a rectangle"
        print "  4 - Draw a pentagon"
        print "  5 - Draw a hexagon"
        print "  6 - Draw an octagon"
        print "  7 - Draw a circle"
        print
        print "  D - Display what was drawn"
        print "  X - Exit"
        print
        choice = raw_input('  Enter your choice: ').strip().upper()
        if choice == 'X':
            break
        elif choice == 'D':
            log.show_log()
        elif choice in ('1', '2', '3', '4', '5', '6', '7'):
            my_shape_num = h_m.how_many()
            if my_shape_num is None:
                continue
            elif my_shape_num == 1:
                d_s.start_point(0, 0)
            else:
                d_s.start_point()
            if choice == '1':
                d_s.draw_triangle(my_shape_num)
            elif choice == '2':
                d_s.draw_square(my_shape_num)
            elif choice == '3':
                d_s.draw_rectangle(my_shape_num)
            elif choice == '4':
                d_s.draw_pentagon(my_shape_num)
            elif choice == '5':
                d_s.draw_hexagon(my_shape_num)
            elif choice == '6':
                d_s.draw_octagon(my_shape_num)
            elif choice == '7':
                d_s.draw_circle(my_shape_num)
            d_s.t.end_fill()
        else:
            print
            print '  Invalid choice: ' + choice
            print


回答4:

How can i use integer and string together? Why can i not set to string than integer?

Well, string formatting is a pretty useful thing:

>>> a_string = "Hi, I'm a string and I'm"
>>> an_integer = 42
>>> another_string = "milliseconds old"
>>> we_are_the_champions = "%s %d %s" % (a_string, an_integer, another_string)
>>> we_are_the_champions
"Hi, I'm a string and I'm 42 milliseconds old"

You even can just throw that integer into string:

>>> champions_are_here = "Hi, I'm a string and I'm even older, I'm %d milliseconds old" % 63
>>> champions_are_here
"Hi, I'm a string and I'm even older, I'm 63 milliseconds old"


回答5:

You can just put your 'letter options' in the except block. That block is executed if the input cannot be converted to an integer, e.g. if it is a letter.


It is however good custom to make your try blocks as small as possible. So it's better to do this:

try:
    choice = int(choice)
except ValueError:
    choice = choice.lower() # Now you don't have to check for uppercase input

You can then check if the choice was an int with type(choice) == int.