9
\$\begingroup\$

Have you ever written a python script, then got to a part where you have to get input from the user? I would assume your code went something like this:

user_input = int(input("Enter number: "))

Then you realized that if the player enters "keyboard spam", then your program crashes. So you need to set up a while-loop exception type thing, like this:

while True:
 try:
 user_input = int(input("Enter number: "))
 break
 except:
 print("you screwed up, only number allowed")

Then, you realize that your inputs must be in between 5 and 23 for the features of your program, so you just code that in too... All in all, this is taking a while and is a pain in the neck!!! What you need is a function that already does all of this for you!

Enter my function:


def get_input(prompt,
 # string conditions
 *conditions, check_alpha=False, must_be_lowercase=False, must_be_uppercase=False,
 conditions_on_input=False, all_conditions_must_be_true=True,
 # now int conditions
 must_be_an_int=False, highest_range=100000, lowest_range=-100000, must_be_odd=False, must_be_even=False):
 if must_be_uppercase and must_be_lowercase:
 raise ValueError("The word cannot be both uppercase and lowercase!!!")
 if must_be_even and must_be_odd:
 raise ValueError("The word cannot be both even and odd!!!")
 while True:
 word_valid = True
 try:
 user_input = input(prompt)
 if must_be_an_int: # if the value must be an int
 try:
 number = int(user_input)
 if not lowest_range < number < highest_range:
 print(f"Values must be between {lowest_range} and {highest_range}!")
 word_valid = False
 continue
 else: # the number is between the specified range
 if must_be_even:
 if not number % 2 == 0:
 print("Number must be even!")
 word_valid = False
 continue
 elif must_be_odd:
 if number % 2 == 0:
 print("Number must be odd!")
 word_valid = False
 continue
 except Exception:
 print("The word must be an integer!", user_input)
 word_valid = False
 continue
 if word_valid:
 return user_input
 else: # if the value must be a string
 if check_alpha: # here we have a boolean. If it is true, we check to make sure only letter are in the
 # user input
 if not user_input.isalpha():
 print("Only alphabetic characters allowed!")
 word_valid = False
 continue
 if conditions_on_input:
 if all_conditions_must_be_true:
 for condition in conditions:
 if condition not in user_input:
 print(f"You have not satisfied the condition that there must be '{condition}!'")
 word_valid = False
 continue
 else:
 condition_found = False
 for condition in conditions:
 if condition in user_input:
 condition_found = True
 break
 if not condition_found:
 print(f"You must satisfy one of the following conditions: ", end="\n")
 for condition in conditions:
 print("'", condition, "'", sep="")
 print()
 word_valid = False
 continue
 if must_be_lowercase:
 for i in user_input:
 if not i.islower():
 print("Letters must be lowercase! Try again!")
 word_valid = False
 continue
 elif must_be_uppercase:
 for i in user_input:
 if not i.isupper():
 print("Letters must be uppercase! Try again!")
 word_valid = False
 continue
 if word_valid:
 return user_input
 except Exception:
 print("No funny business!! Try again!")

Although my function may be a bit chunky, it gets the job done. Note: All of the below features you use as parameters, not modifications to the function itself...

Features:

  • first you put in the prompt, nothing special here

  • next you put in letters (or numbers) you want in the program, each of those as a positional argument. These get classified as conditions. These only apply if the boolean must_be_an_int is False.

  • next, if you want to use the conditions, you must set the bool conditions_on_input to True, whose default value is False.

  • There are two ways to use the conditions. There is a) all of the conditions must be met, or b) only one of the conditions must be met. The way to toggle this is by use of the keyword all_conditions_must_be_true. It's default value is True.

  • Using the keyword check_alpha you can check whether or not the value must only be in the alphabet.

  • Setting the keyword must_be_lowercase to True makes it so that your must phrase must be completely lowercase, same with must_be_uppercase. NOTE: If you set both must_be_uppercase and must_be_lowercase to True, the program will raise an error.

  • Those are all of the string conditions. The next keyword is must_be_an_int. If it is True, it will NOT check any of the string conditions, it will only check the following integer conditions. If it is False, (its default value), it will only check the string and not the ints.

  • highest_range and lowest_range, respectively are the highest and lowest ranges that your number can be. They are by default, one hundred thousand and negative one hundred thousand.

  • must_be_odd and must_be_even. They are self-explanatory. Once again, if both are True, the program will raise an error.

I am thinking of ways to improve this and make this better, thanks for any feedback!

asked Nov 6, 2020 at 20:28
\$\endgroup\$
2
  • \$\begingroup\$ I haven't used Python in a few years, does it provide a type system where all those boolean arguments can be encapsulated into an "options" type argument that is declared beforehand and just passed in? In my experience multiple boolean arguments one after the other will get messed up at some point. \$\endgroup\$ Commented Nov 8, 2020 at 2:00
  • \$\begingroup\$ @Casey You need to use keyword arguments for most of the variables... \$\endgroup\$ Commented Nov 8, 2020 at 12:07

3 Answers 3

6
\$\begingroup\$

Be specific when catching exceptions, and include only the essential, relevant code inside a try-except!

For example:

 if must_be_an_int: # if the value must be an int
 try:
 number = int(user_input)
 if not lowest_range < number < highest_range:
 print(f"Values must be between {lowest_range} and {highest_range}!")
 word_valid = False
 continue
 else: # the number is between the specified range
 if must_be_even:
 if not number % 2 == 0:
 print("Number must be even!")
 word_valid = False
 continue
 elif must_be_odd:
 if number % 2 == 0:
 print("Number must be odd!")
 word_valid = False
 continue
 except Exception:
 print("The word must be an integer!", user_input)
 word_valid = False
 continue
 if word_valid:
 return user_input

The purpose of the try-except is to check that the input is an int! As it stands, the code catches a bunch of random exceptions, and then treats them all as if they meant that the input isn't an int.

 if must_be_an_int: # if the value must be an int
 try:
 number = int(user_input)
 except ValueError:
 print("The word must be an integer!", user_input)
 word_valid = False
 continue
 if not lowest_range < number < highest_range:
 print(f"Values must be between {lowest_range} and {highest_range}!")
 word_valid = False
 continue
 else: # the number is between the specified range
 if must_be_even:
 if not number % 2 == 0:
 print("Number must be even!")
 word_valid = False
 continue
 elif must_be_odd:
 if number % 2 == 0:
 print("Number must be odd!")
 word_valid = False
 continue
 if word_valid:
 return user_input

That code snippet above can be further improved, by removing the superfluous word_valid variable.

 if must_be_an_int: # if the value must be an int
 try:
 number = int(user_input)
 except ValueError:
 print("The word must be an integer!", user_input)
 continue
 if not lowest_range < number < highest_range:
 print(f"Values must be between {lowest_range} and {highest_range}!")
 continue
 else: # the number is between the specified range
 if must_be_even:
 if not number % 2 == 0:
 print("Number must be even!")
 continue
 elif must_be_odd:
 if number % 2 == 0:
 print("Number must be odd!")
 continue
 return user_input

The if statement

if not number % 2 == 0:

can be simplified to

if number % 2 != 0:

which also makes it less ambiguous.


 if must_be_lowercase:
 for i in user_input:
 if not i.islower():
 print("Letters must be lowercase! Try again!")

It's best to keep names like i or j to refer to indices.

Also, you can use str.islower() on the entire string at once, which means the loop is superfluous.

answered Nov 8, 2020 at 1:51
\$\endgroup\$
10
\$\begingroup\$

As a toy to experiment with Python, fine. Please, please don't use this in real life. It's both impossible and counter-productive to try and predict every user input validation scenario, and you're better off writing and using only what you need.

Anyway, specific Python concerns:

condition not in user_input suggests that condition should really just be called substring or needle.

In this loop:

 for i in user_input:
 if not i.isupper():
 print("Letters must be uppercase! Try again!")
 word_valid = False
 continue

Your continue should probably be a break; otherwise you're going to output that error message for every single lowercase letter.

If you want to attempt something that is both simpler and more powerful, accept an iterable of tuples, each a predicate callback and an error message. On every iteration, call every callback, and if it fails, output its error message. If none of the callbacks fails, accept and return the input. Delete all of your range-checking, case-checking and alphanumeric checking logic.

answered Nov 6, 2020 at 21:10
\$\endgroup\$
1
  • 5
    \$\begingroup\$ Adding to Reinderien's answer: It could be in a library that includes predefined predicates for numbers, Yes/No, etc. \$\endgroup\$ Commented Nov 6, 2020 at 23:23
3
\$\begingroup\$

Suggestion: use an appropriate library

There is an amazing amount of Python libraries with tons of usefull functionality. Also for input processing. You can search PyPi for libraries. I found PyInputPlus actively maintained and pretty suitable for your requirements. The function inputInt covers almost all of it.

Your code could (almost!) be reduced to:

import pyinputplus as pyip
user_input = pyip.inputInt(prompt='Enter a number between 5 and 23: ', min=5, max=23)

Checking even and odd values might be done with regexes, which is also supported by inputInt.

answered Nov 8, 2020 at 20:50
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.