I'm new to programming, and I was wondering how I can repeat an input section, if the user types in invalid data.
I want the application to just repeat the input section, instead of having to run the function all over again and making the user type everything all over again.
My guess is that I would have to change the "return main()" into something else.
condition = input("What is the condition of the phone(New or Used)?")
if condition not in ["New", "new", "Used", "used"]:
print("Invalid input")
return main()
gps = input("Does the phone have gps(Yes or No)?")
if gps not in ["Yes", "yes", "No", "no"]:
print("Invalid input")
return main()
You can make a method to check it in a loop:
def check_input(values, message):
while True:
x = input(message)
if x in values:
return x
print "invalid values, options are " + str(values)
You can generalise the code to use a message prompt and a validating function:
def validated_input(prompt, validate):
valid_input = False
while not valid_input:
value = input(prompt)
valid_input = validate(value)
return value
eg:
>>> def new_or_used(value):
... return value.lower() in {"new", "used"}
>>> validate_input("New, or used?", new_or_used)
Or, simpler, but less flexible, pass in the valid values:
def validated_input(prompt, valid_values):
valid_input = False
while not valid_input:
value = input(prompt)
valid_input = value.lower() in valid_values
return value
And use:
>>> validate_input("New, or used?", {"new", "used"})
You could even use the valid values to create the input prompt:
def validated_input(prompt, valid_values):
valid_input = False
while not valid_input:
value = input(prompt + ': ' + '/'.join(valid_values))
valid_input = value.lower() in valid_values
return value
Which gives a prompt:
>>> validate_input("What is the condition of the phone?", {"new", "used"})
What is the condition of the phone?: new/used
Here is a good reading about Control Flows.
Also in your case, you can use strip() and lower() for user inputs.
>>> 'HeLLo'.lower()
'hello'
>>> ' hello '.strip()
'hello'
Here is the solution for Python 3:
while True:
condition=input("What is the condition of the phone(New or Used)?")
if condition.strip().lower() in ['new', 'used']:
break
print("Invalid input")
while True:
gps=input("Does the phone have gps(Yes or No)?")
if gps.strip().lower() in ['yes','no']:
break
print("Invalid input")
Related
def is_digit(x):
if type(x) == int:
return True
else:
return False
def main():
shape_opt = input('Enter input >> ')
while not is_digit(shape_opt):
shape_opt = input('Enter input >> ')
else:
print('it work')
if __name__ == '__main__':
main()
So when the user input a value that is not an integer, the system will repeat the input(). Else, it does something else. But it won't work, may I know why?
Check this. Input always returns a string. So isdigit() is better to use here. It returns True if all characters in a string are digits and False otherwise.
return x.isdigit() will evaluate to True/False accordingly, which will be returned
def is_digit(x):
return x.isdigit()
def main():
shape_opt = input('Enter input >> ')
while not is_digit(shape_opt):
shape_opt = input('Enter input >> ')
else:
print('it work')
if __name__ == '__main__':
main()
An easy way to test if an string is an int is to do this:
def is_digit(x):
try:
int(x)
return True
except ValueError:
return False
You must use try except when trying to convert to int.
if it fails to convert the data inside int() then throw the exception inside except which in your case with makes the loop continue.
def is_digit(x):
try:
int(x)
return True
except:
return False
def main():
shape_opt = input('Enter input >> ')
while not is_digit(shape_opt):
shape_opt = input('Enter input >> ')
else:
print('it work')
if __name__ == '__main__':
main()
status_login = False
use_atm = "y"
data =[{"norek":932012042,
"pin":123,
"name":"grizly",
"bank":"BCA",
"balance":5000000},
{"norek":932012052,
"pin":1234,
"name":"Basuki Pepeh",
"bank":"BRI",
"balance":4000000},
{"norek":932012099,
"pin":1235,
"name":"Bambang Gentolet",
"bank":"Mandiri",
"balance":3500000}]
def cek_login(p):
for login in data:
if login['pin'] == p:
return login
return False
while use_atm == "y":
while status_login == False:
print("Welcome to ATM")
print("insert your pin")
pin = input("PIN : ")
if cek_login(pin) != False:
print("welcome "+cek_login(pin)['name'])
status_login = True
else:
print("")
print("Ops Your PIN is wrong")
print("")
print("")
I want to make a login using a pin but why the result is always wrong, what is wrong with the code above
Welcome to ATM
insert your pin
PIN : 123
Ops Your PIN is wrong
The pin in your data is int.
input is str.
str is not equal to int.
pin = int(input("PIN : "))
Your comparison is failing in cek_login, so print the values using repr() and see why they don't compare. Note that repr() gives a debugging representation of the data so it is easier to spot differences:
def cek_login(p):
for login in data:
print(repr(long['pin']),repr(p)) # see why they don't compare
if login['pin'] == p:
return login
return False
You will see:
Welcome to ATM
insert your pin 123
123 '123' # note integer versus string (quoted).
1234 '123'
1235 '123'
Convert the input to an integer to fix the problem.
pin = int(input("PIN : "))
You'll also find that you will enter an infinite loop after getting the pin correct, because use_atm never changes.
Learn to use a source debugger so you don't have to litter your code with print statements.
There were a couple bugs here, and some other people already pointed them out:
cek_login(p) uses data, which is a list of dictionaries, so you need to first access the dictionary in the list by index ([0], [1], ...), and THEN access the dictionary element with bracket notation like ['pin']
data[0]['pin'] is an int, whereas the result of input() is a str, so you need to CONVERT the str to an int with int(my_str_input)
Similar to point 1, the print statement under the while loop which prints the user's name needs to first access the index in the list (data) before accessing the dictionary value ([name])
Try this:
status_login = False
use_atm = "y"
data =[{"norek":932012042,
"pin":123,
"name":"grizly",
"bank":"BCA",
"balance":5000000},
{"norek":932012052,
"pin":1234,
"name":"Basuki Pepeh",
"bank":"BRI",
"balance":4000000},
{"norek":932012099,
"pin":1235,
"name":"Bambang Gentolet",
"bank":"Mandiri",
"balance":3500000}]
def cek_login(p):
print(int(p))
print(data[0]['pin'])
if data[0]['pin'] == int(p):
return True
return False
while use_atm == "y":
while status_login == False:
print("Welcome to ATM")
print("insert your pin")
pin = input("PIN : ")
if cek_login(pin) != False:
print("welcome "+ data[0]['name'])
status_login = True
use_atm = "n"
else:
print("")
print("Ops Your PIN is wrong")
print("")
print("")
Both functions use the same check(x) function and almost identical to each other, except the argument the second function have to take in order to use print.
Entering int as inputs showed no problem.
However, if alphabets were entered, the return result of enter_num() becomes NoneType, but this does not happen in enter_amount().
Where and how did it went wrong?
def check(x): #check if user input is integer
try:
int(x)
return True
except ValueError:
return False
def enter_num(): #get user input for lotto numbers
x = input("buy num:")
if check(x) == True: #check int
x = int(x)
return x
else:
print("Please enter integer")
enter_num()
def enter_amount(x): #get user amount of the lottos
print(x) ##if enter_num errored once, this will show None##
y = input("How many?")
if check(y) == True: #check int
y = int(y)
print("%s for %s copies" % (x,y))
return y
else:
print("Please enter integer")
enter_amount(x)
buy_num = enter_num()
amount = enter_amount(buy_num)
You never return the recursive result from enter_num():
def enter_num():
x = input("buy num:")
if check(x) == True:
x = int(x)
return x
else:
print("Please enter integer")
enter_num() # ignoring the return value
# so None is returned instead
The same applies to enter_amount(); it too ignores the recursive call.
You need to explicitly return the recursive call result, just like you would for any other expression:
def enter_num():
x = input("buy num:")
if check(x) == True:
x = int(x)
return x
else:
print("Please enter integer")
return enter_num() # ignoring the return value
Do the same for enter_amount(); change the last line to return enter_amount(x).
You really should not be using recursion however; all the user has to do is hold the ENTER key for a short amount of time for your code to end up breaking the recursion limit. See Asking the user for input until they give a valid response for better techniques; a while loop would be fine here.
There is also no need to test for == True; if already tests for truth:
if check(x):
I'd also inline the check test; no need to convert to int() twice if the string can be converted. The following won't run out of recursion depth, but just returns int(x) directly if x contained a convertible value, or prints an error message otherwise and loops right back to ask for the number again:
def enter_num():
while True:
x = input("buy num:")
try:
return int(x)
except ValueError:
print("Please enter integer")
I'm often tasked with asking users for input. I've always just written my prompts "as-needed" in my main execution scripts. This is kind of ugly, and because I often ask for the same types of input across multiple scripts, a ton of my code is just copy/pasted prompt loops. Here's what I've done in the past:
while True:
username = input("Enter New Username: ")
if ldap.search(username):
print " [!] Username already taken."
if not validator.validate_username(username):
print " [!] Invalid Username."
else:
break
I'd like to create something that can be called like:
username = prompt(prompt="Enter New Username: ",
default=None,
rules=["user_does_not_exist",
"valid_username"])
Then the prompt function looks like:
def prompt(prompt, default, rules):
while True:
retval = input(prompt)
if default and retval == "":
break
return default
if not rule_match(retval, rules):
continue
break
return retval
def rule_match(value, rules):
if "user_does_not_exist" in rules:
if not user.user_exists(value):
return False
if "valid_username" in rules:
if not validator.username(value):
return False
if "y_n_or_yes_no" in rules:
if "ignore_case" in rules:
if value.lower() not in ["y", "yes", "n", "no"]:
return False
else:
if value not in ["y", "yes", "n", "no"]:
return False
return True
An alternative I'm considering is to make a Prompt class, which would allow for more flexibility with the results. For example, if I want to convert the "y" or "n" to True or False, the above doesn't really work.
create_another = Prompt(prompt="Create another user? (y/n): ,"
default=False,
rules=["y_n_or_yes_no",
"ignore_case"]).prompt().convert_to_bool()
The other alternative I'm considering is just making individualized prompts and naming them, with each one written similar to my very original code. This doesn't actually change anything. It just serves to get these loops out of my main execution code which makes the main execution code easier to browse:
username = prompt("get_new_username")
def prompt(prompt_name):
if prompt_name == "get_new_username":
while True:
username = input("Enter New Username: ")
if ldap.search(username):
print " [!] Username already taken."
if not validator.validate_username(username):
print " [!] Invalid Username."
else:
break
return username
if prompt_name == "y_n_yes_no_ignore_case":
# do prompt
if prompt_name == "y_n_yes_no":
# do prompt
if prompt_name == "y_n":
# do prompt
if prompt_name == "y_n_ignore_case":
# do prompt
if prompt_name == "yes_no":
# do prompt
if prompt_name == "yes_no_ignore_case":
# do prompt
I realize that it's probably just a good idea to settle on one accepted "y/n" format for all of my programs, and I will. This is just for the sake of showing that, in cases where I would need a very similar but slightly different prompt, it would result in a lot of copy/pasted code (no flexibility with this method at all).
What is a good approach to writing clean, flexible, and easy-to-maintain user prompts?
(I've seen this: Asking the user for input until they give a valid response and some other responses. My question is not about how to get input and validate it, it's about how to make a flexible input system that can be reused with across multiple programs).
I once wrote a function for something similar. The explanation is in the doc-string:
def xory(question = "", setx = ["yes"], sety = ["no"], setz = [], strict = False):
"""xory([question][, setx][, sety][, setz][, strict]) -> string
Asks question. If the answer is equal to one of the elements in setx,
returns True. If the answer is equal to one of the elements in sety,
returns False. If the answer is equal to one of the elements in setz,
returns the element in setz that answer is equal to. If the answer is
not in any of the sets, reasks the question. Strict controls whether
the answer is case-sensitive. If show is True, an indication of the
acceptable answers will be displayed next to the prompt."""
if isinstance(setx, str):
setx = [setx]
if isinstance(sety, str):
sety = [sety]
if isinstance(setz, str):
setz = [setz]
if (setx[0])[0] != (sety[0])[0]:
setx = [(setx[0])[0]] + setx
sety = [(sety[0])[0]] + sety
question = question.strip(" ") + " "
while True:
if show:
shows = "[%s/%s] " % (setx[0], sety[0])
else:
shows = ""
user_input = raw_input(question + shows)
for y in [setx, sety, setz]:
for x in y:
if (user_input == x) or ((not strict) and (user_input.lower() == x.lower())):
if y is setx:
return True
elif y is sety:
return False
else:
return x
question = ""
show = True
Examples:
>>> response = xory("1 or 0?", ["1", "one", "uno"], ["0", "zero", "null"], ["quit", "exit"])
1 or 0? x
[1/0] eante
[1/0] uno
>>> print(response)
True
>>> response = xory("Is that so?")
Is that so? Who knows?
[y/n] no
>>> print(response)
False
>>> response = xory("Will you do it?", setz=["quit", "exit", "restart"])
Will you do it? hm
[y/n] quit
>>> print(response)
quit
I'd advise to write a library that contains a number of very clearly defined building blocks, each one as small and light-weight as possible, without too many assumptions baked in about how you're going to put the pieces together.
That is, I'd include one function that does the loop, but instead of passing in a set of rules, I'd allow for exactly one function to be passed in that either returns a value (if a valid input was given and after converting it in any way necessary) or raises a ValueError if the input wasn't usable. Other building blocks would implement certain checks or transformations (like resolution of 'y' and 'n' into boolean values).
This way, you would leave it completely up to the user to assemble the stuff in a way suitable for the use case.
# library:
def prompt(prompt, default, postprocess):
value = input('{} ({}): '.format(prompt, default)) or default
try:
return postprocess(value)
except ValueError:
continue
def check_lower(value):
if not value.islower():
raise ValueError()
def to_bool(value):
return value in 'yes'
# using the library:
def postprocess(value):
check_lower(value)
return to_bool(value)
prompt('Really?', 'n', postprocess)
I would create a prompt function as such:
def prompt(prompt, default=None, rules=[]):
while True:
response = input(prompt)
if response:
valid = [rule(response) for rule in rules]
if not(False in valid):
return response
else:
print('Invalid input')
else:
return default
You could then create different validation functions such as
def filterValidEmail(string):
if '#' in string:
if '.' in string.split('#')[1]:
return True
else:
return False
else:
return False
And call these functions like so:
prompt('What is your email? ', rules=[filterValidEmail])
You could also tweak this so that you can tell the user what verification they failed or disallow blank inputs.
I'm trying to have a program return the luminance of a light based on user given values, but the while loop is what's getting me confused.
def Brightness(r,g,b):
value = 0.2126 * r + 0.7152 * g + 0.0722 * b
return value
while True:
r = int(raw_input("Enter in the value for red: "))
g = int(raw_input("Enter in the value for green: "))
b = int(raw_input("Enter in the value for blue: "))
result = Brightness(r,g,b)
print result
if ((r == "quit") or (g == "quit") or (b == "quit")):
break
result = Brightness(r,g,b)
I feel like the answer is staring me right in the face, but I can't put my finger on it.
First of all, notice that you're doing int(raw_input()) everywhere. This means that when you try to enter quit as input, the program will raise a ValueError exception and quit abnormally.
My suggestion is to modify a little bit the input loop:
while True:
i1 = raw_input("Enter in the value for red: ")
i2 = raw_input("Enter in the value for green: ")
i3 = raw_input("Enter in the value for blue: ")
if ((i1 == "quit") or (i2 == "quit") or (i3 == "quit")):
break
r = int(i1)
g = int(i2)
b = int(i3)
result = Brightness(r,g,b)
print result
result = Brightness(r,g,b)
Also I don't understand what the last line result = Brightness(r,g,b) is for? It seems to me you're outputting the result in the last two lines in the loop.
Hope this helps!
The problem is that you typecast all your user input to an int without validating the input. When the input is a string, python will throw an error, because a string cannot be converted to an integer.
Best would be to store the raw input as raw data into the variables r, g, and b and check whether the user wants to quit first. If not, you should do a sanity check on your input values. If they are integers, you can typecast them as such. Otherwise, print an error message or start at the beginning of the loop.
This code can't possibly work correctly. If the user enters quit, then when you pass that input string to int() you will get an exception, because quit doesn't work as an integer.
Then, if the user does enter quit, you can break out of the loop without setting all of r, g, and b, so the last line where you call Brightness(r,g,b) will raise an exception.
Also, this doesn't check to see if the user entered quit until after the user has been prompted three times. This will annoy a user who wanted to quit at the first number.
So, don't convert the user's input to integer without checking for quit; check after each time you prompt the user; and either have default values for r,g, and b or else don't have the last call to Brightness() if the user quit.
QUIT = "quit"
got_input = False
while True:
r = raw_input("Enter in the value for red: ")
if r == QUIT:
break
r = int(r)
g = raw_input("Enter in the value for green: ")
if g == QUIT:
break
g = int(g)
b = raw_input("Enter in the value for blue: ")
if b == QUIT:
break
b = int(b)
got_input = True
result = Brightness(r,g,b)
print result
if got_input:
result = Brightness(r,g,b)
Note that there is a lot of repetition in this loop. Perhaps we could write a function to clean it up?
def get_user_input(question)
answer = raw_input(question)
if answer == QUIT:
return False, None
else:
return True, int(answer)
got_input = False
while True:
quit, r = get_user_input("Enter in the value for red: ")
if quit:
break
quit, g = get_user_input("Enter in the value for green: ")
if quit:
break
b = get_user_input("Enter in the value for blue: ")
if quit:
break
got_input = True
result = Brightness(r,g,b)
print result
if got_input:
result = Brightness(r,g,b)
It's a little better. Maybe we can make it cleaner? Let's use Python exceptions. We can just write the code to assume that things are going as planned, but when the user enters quit we can raise an exception. Let's define our own exception to make this clear.
import sys
QUIT = "quit"
class UserHasQuit(Exception):
pass
def get_user_input(question)
answer = raw_input(question)
if answer == QUIT:
raise UserHasQuit
return int(answer)
try:
r = get_user_input("Enter in the value for red: ")
g = get_user_input("Enter in the value for green: ")
b = get_user_input("Enter in the value for blue: ")
result = Brightness(r,g,b)
print result
except UserHasQuit:
sys.exit(0)
Instead of raise UserHasQuit we could call sys.exit() when getting user input. But when you are debugging, an exception gives you a stack backtrace that shows you where the exception came from, while sys.exit() just makes the program stop. So if you have an unexpected sys.exit() it can be a bit hard to find it, but exceptions are easy to find. So it's best to keep calls to sys.exit() at the outer layer and use exceptions for internal code.