Python Forum
Forcing input from pre-defined list. - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: General Coding Help (https://python-forum.io/forum-8.html)
+--- Thread: Forcing input from pre-defined list. (/thread-19189.html)

Pages: 1 2


Forcing input from pre-defined list. - scotty501 - Jun-17-2019

Hello All,

I am stumbling into the world of programming, and have started by watching videos, and reading beginner instructions on Python. I have just started with Hello World, and using an online lessons, a basic calculator script. I have ventured off and wondering if I can make it a little more functional and what does what.


Below is the code, the question is, how would you make it, that the user has to select the pre-defined options, and if not, it gives an error message like, try again selecting from he above options? Right now the script will run to the end until the error comes back. What if you wanted to force the input to be a number, and the same again, if not, error message and start again?

Lastly, can you make the response ignore the capitalisation - so "Multiply" or "multiply" is acceptable?

I do appreciate this is basic stuff, so be kind :)

number1 = int(input("What is the first number?"))
operator = input("What do you want to do + Plus - Minus * Multiply / Divide or p Power of? ")
number2 = int(input("What is the second number?"))

if operator == "+":
    answer = number1+number2
elif operator == "-":
    answer = number1-number2
elif operator == "*":
    answer = number1*number2
elif operator == "/":
    answer = number1/number2
if operator == "Plus":
    answer = number1+number2
elif operator == "Minus":
    answer = number1-number2
elif operator == "Multiply":
    answer = number1*number2
elif operator == "Divide":
    answer = number1/number2
elif operator == "p":
    answer = number1**number2
elif operator == "power of":
    answer = number1**number2

print("The answer is " + str(answer))



RE: Forcing input from pre-defined list. - metulburr - Jun-18-2019

(Jun-17-2019, 12:46 PM)scotty501 Wrote: What if you wanted to force the input to be a number, and the same again, if not, error message and start again?
https://python-forum.io/Thread-Validating-User-Input

(Jun-17-2019, 12:46 PM)scotty501 Wrote: Lastly, can you make the response ignore the capitalisation - so "Multiply" or "multiply" is acceptable?
str.lower() will change your string to all lowercase. Then you only have to compare the lowercase .

(Jun-17-2019, 12:46 PM)scotty501 Wrote: how would you make it
I would use dictionaries, not a massive if/elif condition
number1 = int(input("What is the first number?"))
operator = input("What do you want to do + Plus - Minus * Multiply / Divide or p Power of? ")
number2 = int(input("What is the second number?"))

calculate = {
    '+':number1 + number2,
    '-':number1 - number2,
}

print(calculate[operator])



RE: Forcing input from pre-defined list. - Skaperen - Jun-18-2019

i want to see a pythonic example of the use of a dictionary in place of a massive sequence if if/elif clauses.


RE: Forcing input from pre-defined list. - SheeppOSU - Jun-18-2019

Here is a good example of what you are trying to achieve -
def getNum(phrasing):
    while True:
        num1 = input(phrasing + '\n')
        try:
            int(num1)
            break
        except:
            print('Invalid response')
    return num1

opIndex = {'1' : '+', '2' : '-', '3' : '*', '4' : '/', '5' : '%'}

def getOp():
    while True:
        operator = input('Pick from the following operators calling them by their index numbers\n1. "+" (Add)\n2. "-" (Subtract)\n3. "*" (Multiply)\n4. "/" (Division)\n5. "%" (Modulous)\n')
        try:
            errorMessage = 'Please type a valid answer'
            int(operator)
            errorMessage = 'Please type a number in the range 1-5'
            assert operator in opIndex
            break
        except:
            print(errorMessage)
    return opIndex[operator]

def main():
    num1 = getNum('Type your first number')
    operator = getOp()
    num2 = getNum('Type your second number')
    answer = eval('%s %s %s' %(num1, operator, num2))
    print('%s %s %s = %s' %(num1, operator, num2, answer))

if __name__ == '__main__':
    main()



RE: Forcing input from pre-defined list. - scotty501 - Jun-18-2019

Thanks for all the great responses, I am trying to adapt my sample code as I want to learn what is happening, rather than just copy someone else code. I am playing with the first suggestion and using the sample code below.

while True:
    text = input('Please enter an integer: ')
    if text.isdigit():
        number = int(text)
        break
    print('{} is not an integer.'.format(text))
print((number + 1) * 2)
This breaks my original code as I would expect.

Q1 - Would that be because (int input) will not work with text.isdigit, so the only way I can get the two to play nice is to make it a input not int input, which then of means, my if and elif statement does not work as it does not see it as an integer?
Q2 - How would you fix it?

As I say, I am trying to avoid just copying peoples code, and understand each part first. (Very early days).

My revised code, which does not output integer as below:

while True:
    number1 = input('Please enter the first number: ')
    if number1.isdigit():
        number = int(number1)
        break
    print('{} is not a number! Try again.'.format(number1))

operator = input("What do you want to do + Plus - Minus * Multiply / Divide or p Power of? ")

while True:
    number2 = input('Please enter the second number: ')
    if number2.isdigit():
        number = int(number2)
        break
    print('{} is not a number! Try again.'.format(number2))

if operator == "+":
    answer = number1+number2
elif operator == "-":
    answer = number1-number2
elif operator == "*":
    answer = number1*number2
elif operator == "/":
    answer = number1/number2
if operator == "Plus":
    answer = number1+number2
elif operator == "Minus":
    answer = number1-number2
elif operator == "Multiply":
    answer = number1*number2
elif operator == "Divide":
    answer = number1/number2
elif operator == "p":
    answer = number1**number2
elif operator == "power of":
    answer = number1**number2

print("The answer is " + str(answer))



RE: Forcing input from pre-defined list. - noisefloor - Jun-18-2019

Hi,

Quote: Here is a good example of what you are trying to achieve -
Actually not, as elval is evil. And completely unneccessary here. Simply use the operator module of the standard lib.

Simplified / shorten approach:

from operator import add, sub, mul, truediv

operations = {'+': add, '-': sub, '*': mul, '/': truediv}

def get_operation(op):
    if op not in operations.keys():
        return False
    else:
        return operations[op]

def get_number(num):
    if not num.isdigit():
        return False
    else:
        return int(num)

number_1 = input('input 1st number: ')
number_1 = get_number(number_1)
op = input('Input operator (+ - * /): ')
op = get_operation(op)
number_2 = input('input 2nd number: ')
number_2 = get_number(number_2)
if all([number_1, number_2, op]):
    result = op(number_1, number_2)
    print(result)
else:
    print('invalid input at some point...')



RE: Forcing input from pre-defined list. - scotty501 - Jun-18-2019

Thanks - that makes sense for me to replace the "if and elif" commands, but apart from keeping the code smaller, does it matter?

I still have the issue where the output from my code (see 2 posts up) is not an integer, which I assume is because the "input" is being used, not "int (input).


RE: Forcing input from pre-defined list. - perfringo - Jun-18-2019

Word of caution about str.isdigit(). Code will blow up spectacularly in corner cases:

>>> '1²'.isdigit()                                                         
True
>>> int('1²')
/../
ValueError: invalid literal for int() with base 10: '1²'
I would prefer try-except (try to convert to int, except in ValueError do something)


RE: Forcing input from pre-defined list. - scotty501 - Jun-18-2019

(Jun-18-2019, 11:28 AM)perfringo Wrote: Word of caution about str.isdigit(). Code will blow up spectacularly in corner cases:

>>> '1²'.isdigit()                                                         
True
>>> int('1²')
/../
ValueError: invalid literal for int() with base 10: '1²'
I would prefer try-except (try to convert to int, except in ValueError do something)

Thanks for replying, but I am even more confused! (Remember I am newbie).


RE: Forcing input from pre-defined list. - perfringo - Jun-18-2019

For newbie Smile

Before you start coding you must have idea (a.k.a algorithm) how to solve problem at hand. If you don't have solution which can be expressed in spoken language there are very slim chances that you will be able to write code which delivers expected results.

Following requires 3.6 <= Python as f-strings are used.

task: 'take from user two numbers and operation and perform calculation and print it out'

Task should be broken into smaller subtasks:

- how to take user input
- how to perform calculation
- how to print result

Little thinking about taking user input makes it obvious, that numbers must be validated as well as operator (we don't want to multiply 'a' with 'b' etc).

So how do we validate? In Python it is usually done by try...except. This is called EAFP style:

Quote:EAFP
Easier to ask for forgiveness than permission. This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many try and except statements. The technique contrasts with the LBYL style common to many other languages such as C.


Let's define that we will accept only integers. We should try to convert user input into int and in case of ValueError (user input is not convertible to int) we inform user that int must be entered. We wrap it into while True loop which will continue until value which can be converted into integer is entered (alternatively we might limit number of entrances or some specific input which cancels the loop).

Something like that:

def validate(request):
    while True:
        answer = input(request)
        try:
            return int(answer)
        except ValueError:
            print(f'Expected integer but input was {answer}')
Parameter 'request' is useful if we need different texts displayed for user:

validate('Enter first number: ')
validate('Enter second number: ')
If you prefer, you can do it as oneliner:

first, second = validate('Enter first number: '), validate('Enter second number: ')
Now about validating operator. As noisefloor already showed, easiest way is to use built-in operator module with dictionary. As we already have validate function we should consider whether we try to modify it to use for operator validation as well or write standalone function. Let's try latter:

import operator

ops = {'+': operator.add, '-': operator.sub, '*': operator.mul, '**': operator.pow, '/': operator.truediv}

def operation(request):
    while True:
        answer = input(request)
        try:
            return ops[answer]
        except KeyError:
            print(f'Expected operator but input was {answer}')
As you can see these functions are quite similar and they actually can be merged into one but I think as this is for newbie we will pass this option (you will see below why ops dictionary is outside of function)

Now we have user input validation for integers and operator and we are ready to roll:

first, second = validate('Enter first number: '), validate('Enter second number: ')
op = operation(f"Enter operation (one from: {', '.join(ops.keys())}): ")   # ensures that only defined operators are listed
print(f'Answer is {op(first, second)}')