So, I've written a function that is meant to take a password, userID, and pin, match it to an account(in the prewritten dictionary) and validate it, but if three wrong info is inputted, it shall end the while loop. However, after i input the password, user ID and pin, it just stops with exit code 0. Can anyone explain why?
info = {"password0": "FelixIscool3103",
"PIN0": "3456",
"UserID0": "Felixx",
"password1": "JohnSmith348",
"PIN1": "7845",
"UserID1": "JohnSmithy99"}
def checker(password, user_id, pin, strike_pass, strike_id, strike_pin):
while strike_id and strike_pass and strike_pin != 3:
for i in range(0, len(info), 3):
if password == info["password"+str(i)]:
return print("Correct password")
break
else:
strike_pass + 1
return print("Password is incorrect")
for x in range(2, len(info), 3):
if user_id == info["UserID"+str(i)]:
return print("Correct user ID")
break
else:
strike_id + 1
return print("Wrong user ID")
for y in range(1, len(info), 3):
if pin == info["PIN" + str(i)]:
return print("Correct user ID")
break
else:
strike_pin + 1
return print("PIN incorrect")
checker(input("Enter your password"),
input("Enter your user ID"),
input("Enter your pin"),
0, 0, 0)
The numeric value 0 is "falsey" in Python. And you're passing a 0 for strike_id and strike_pass. So the while condition is evaluating to:
while False and False and True:
Which is, of course, false.
It looks like your intent was to compare each of those values to 3, but you only compared one of them:
while strike_id != 3 and strike_pass != 3 and strike_pin != 3:
The while loop continues while the condition is True.
As strike_id and strike_pass and strike_pin != 3 is False it never enters the while loop.
I am not really sure what the statement should check, but maybe try:
limit = 3
while all((strike_id <=limit, strike_pass <= limit, strike_pin <= limit)):
However, make sure the strikes get added correctly.
Related
I was making a program that uses a while loop and if operators that gives the user 3 attempts to write a password, im trying to add an attempt counter that displays how many attempts you have left by subtracting the variables
I tried using a variable: Attempts_left to give me the number of attempts left by subtracting Max_attempts by Attempt\_ count
super_password = "survive"
attempt = ""
Attempt_count = 0
Max_attempts = 3
Attempts_left = Max_attempts-Attempt_count
Out_of_attempts = False
while super_password != attempt and not(Out_of_attempts):
if Attempt_count < Max_attempts:
Attempt_count += 1
attempt = input("Enter password, " + str(Attempts_left) + " attempts left: ")
else:
Out_of_attempts = True
if Out_of_attempts:
print("Access denied")
else:
print("Access granted")
But it would always display: Enter password, 3 attempts left:
Here's an updated version of your code that should work as you intended:
super_password = "survive"
attempt = ""
attempt_count = 0
max_attempts = 3
out_of_attempts = False
while super_password != attempt and not(out_of_attempts):
attempts_left = max_attempts - attempt_count
if attempt_count < max_attempts:
attempt_count += 1
attempt = input("Enter password, " + str(attempts_left) + " attempts left: ")
else:
out_of_attempts = True
if out_of_attempts:
print("Access denied")
else:
print("Access granted")
The issue in your original code is that you were defining the Attempts_left variable before the loop starts, and you were not updating it inside the loop. Therefore, it always remained the same value (3). In the updated version, I moved the definition of attempts_left inside the loop and updated it before each input request. This way, it will be calculated based on the current attempt_count value and will always be accurate.
Continuing from yesterday, i was implementing a basic exception handler, to deal with every type of error from the user, using conditional if and elif statements. It was fine until my program wrongly thinks that integer inputs are non-integers (for the age field), and this stops me from continuing my program, as other functions are dependant on this. Here is the code snippet:
def submit():
username = UserName.get()
firstname = User_FirstName.get()
surname = User_Surname.get()
age = User_Age.get()
height = User_Height.get()
weight = User_Weight.get()
data = [username, firstname, surname, age, height, weight]
flag = False
while flag == False:
if len(username) == 0:
messagebox.showerror('Project Pulse', 'Please ensure the "Username" field is not left blank')
break
elif type(firstname) != str:
messagebox.showerror('Project Pulse', 'Please ensure there are no numbers in the "First Name"')
break
elif len(firstname) == 0:
messagebox.showerror('Project Pulse', 'Please ensure that the "First Name" field is not left blank')
break
elif len(surname) == 0:
messagebox.showerror('Project Pulse', 'Please ensure that the "Last Name" field is not left blank')
break
elif type(surname) != str:
messagebox.showerror('Project Pulse', 'Please ensure there are no numbers in the "Surname"')
break
elif len(age) == 0:
messagebox.showerror('Project Pulse', 'Please ensure the "age" field is not left blank')
break
elif type(age) != int:
messagebox.showerror('Project Pulse', 'Please ensure only integers are input in "Age"')
break
...
else:
flag = True
Here, submit is a button. The elif statements continue for a few more lines, but the point is, the program does not run past the 'age' line, i.e. the error message box : 'Please ensure only integers are input in "Age" displays, no matter what. I tried printing the actual age variable, and i got an integer, so I can't seem to find the issue!
I think your issue might be that get() treats every user input as a string.
In other words, even though the user might want to say 22, the computer would read it as "22".
To safeguard against this, you can try putting int() around User_Age.get():
int(User_Age.get())
Hope this helps!
-VDizz
Like #VDizz, I am assuming your values are coming from tk.Entry widgets. Therefore all values will be strings. So the casting will work but this will also generate an error in your elif block. To avoid the error causing a new issue you might try writing a small function to check the type. As per this stack overflow response https://stackoverflow.com/a/9591248/6893713.
Hope this helps too.
In response to #Guilherme Iazzete's comment (slightly modified from the link):
def intTryParse(value):
try:
int(value)
return True
except ValueError:
return False
username = 'HarvB'
age = '4'
flag = False
while flag == False:
if len(username) == 0:
messagebox.showerror('Project Pulse', '...')
break
...
elif intTryParse(age) is False:
messagebox.showerror('Project Pulse', '...')
break
...
else:
flag = True
I'm trying to make a program where I input a name and a surname and the code checks if the name is invalid (list of invalidiations below). If it has any invalidations, it asks me to say the name again and presents me a list of all the invalidations.
Invalidations list (I'll show the code version too):
- The name has digits
- The name has symbols
- The name has no spaces
- It has more then one space
- One of the names is either too short or too long
- The first letter of the name is a space
- The last letter of the name is a space
I can't use exceptions here, because these are not code erros. I've made it with Ifs, but it got to a point where there a simply lots of Ifs for it to be viable.
def has_digits(name):
digits = any(c.isdigit() for c in name)
if digits == True:
return True
print("Your name has digits.")
else:
return False
def has_symbols(name):
symbols = any(not c.isalnum() and not c.isspace() for c in name)
if symbols == True:
return True
print("Your name has symbols.")
else:
return False
def has_no_spaces(name):
spaces = any(c.isspace() for c in name)
if not spaces == True:
return True
print("You only gave me a name.")
else:
return False
def many_spaces(name):
m_s = name.count(' ') > 1
if m_s == True:
return True
print("Your name has more than one space.")
else:
return False
def unrealistic_length(name, surname):
length= (float(len(name)) < 3 or float(len(name)) > 12) or float(len(surname)) < 5 or float(len(surname) > 15)
if length == True:
return True
print("Your name has an unrealistic size.")
else:
return False
def first_space(name):
f_s = name[0] == " "
if f_s == True:
return True
print("The first letter of your name is a space.")
else:
return False
def last_space(name):
l_s = name[-1] == " "
if l_s == True:
return True
print("The last letter of your name is a space.")
else:
return False
name = "bruh browski"
namesplit = name.split(" ")
name1 = namesplit[0]
name2 = namesplit[1]
print(has_digits(name))
print(has_symbols(name))
print(has_no_spaces(name))
print(many_spaces(name))
print(unrealistic_length(name1, name2))
print(first_space(name))
print(last_space(name))
Maybe the prints shouldn't be in the defs themselves. I don't know. I'm almost sure doing a for loop is the way to go, but I just can't imagine how to do it.
Result:
False
False
False
False
False
False
False
The methods you've used to define exactly what counts as each "invalidation" will have to stay, unless you can replace them with something else that does the same thing. But you can check all of those conditions at once using a generator expression:
if any(is_invalid(name) for is_invalid in [
has_digits, has_symbols, has_no_spaces, many_spaces, unrealistic_length, first_name, last_name
]):
# then this string is invalid
# otherwise, all of those returned false, meaning the string is valid.
You can then use that condition to determine when to stop asking the user, or however else you need to.
If you wanted to not individually define all those functions, you could also maybe use lambdas to do the same thing.
As a sidenote, before actually using this in production for checking the validity of names, I advise having a look at the list of Falsehoods Programmers Believe about Names. It's a fun read even if it's not relevant to your use case, though.
You could have a single function which calls all of your other functions and handles it appropriately.
def master_verify(name):
# Put all your verify functions in the list below.
verify_funcs = [has_digits, has_symbols, has_no_spaces, many_spaces,
unrealistic_length, first_space, last_space]
# It will return True if any your functions return True. In this case,
# returning True means the name is invalid (matching your other
# function design). Returning False means the name is valid.
return any(is_invalid(name) for is_invalid in verify_funcs)
Since you mentioned you want the program to find any name errors and ask the user to try again, we can write a loop to handle this.
def get_name():
while True:
# Loop until you get a good name
name = input("Enter your name: ").strip()
if master_verify(name):
# Remember, if True this means invalid
print("Invalid name. Try again.")
continue # continue jumps to the top of a loop, skipping everything else.
return name # Will only get here if the name is valid.
I also suggest you should do the name and surname split inside your unrealistic_length function.
Then, all you need to do is
name = get_name()
# All of the validation has already happened.
print(f"The correct and validated name is: {name}")
Last but not least, anything in a function after a return is unreachable. So a lot of your prints will never happen. Put the print statements before your return.
Alright. I've managed to do it by myself. I still fill there's a better way to do it, but this is the way I found.
errors_list = []
print("Hi. Tell me your first and last name.")
def choose_name(name):
global fname
global sname
fname = ""
sname = ""
global errors_list
try:
no_letters = any(c.isalpha() for c in name)
no_spaces = name.count(" ") == 0
digits = any(c.isdigit() for c in name)
symbols = any(not c.isalnum() and not c.isspace() for c in name)
many_spaces = name.count(" ") > 1
first_space = name[0] == " "
last_space = name[-1] == " "
if no_letters == False:
errors_list.append("It has no letters")
if no_spaces == True:
errors_list.append("It has no spaces")
else:
namesplit = name.split(" ")
fname = namesplit[0]
sname = namesplit[1]
pass
if fname and sname is not "":
bad_length = (float(len(fname)) < 3 or float(len(fname)) > 12) or float(len(sname)) < 4 or float(len(sname) > 15)
if bad_length == True:
errors_list.append("One of your names has an unrealistic size")
pass
else:
bad_length = (float(len(name)) < 3 or float(len(name)) > 12)
if bad_length == True:
errors_list.append("It has an unrealistic size")
pass
if digits == True:
errors_list.append("It has digits")
pass
if symbols == True:
errors_list.append("It has symbols")
pass
if many_spaces == True:
errors_list.append("It has more than one space")
pass
if first_space == True:
errors_list.append("The first letter is a space")
pass
if last_space == True:
errors_list.append("The last letter is a space")
pass
except IndexError:
print("You must write something. Try again.")
name = input("My name is ").title()
choose_name(name)
name = input("My name is ").title()
choose_name(name)
while True:
if len(errors_list) != 0:
print("Your name has these errors:")
for i in errors_list:
print(" " + str(errors_list.index(i) + 1) + "- " + i + ".")
print("Try again.")
errors_list.clear()
name = input("My name is ").title()
choose_name(name)
else:
print("Nice to meet you, " + fname + " " + sname + ".")
break
Result when I type the name '----... '
Hi. Tell me your first and last name.
My name is ----...
Your name has these errors:
1- It has no letters.
2- It has symbols.
3- It has more than one space.
4- The last letter is a space.
Try again.
My name is
This question already has answers here:
How to break out of while loop in Python?
(5 answers)
Closed 4 years ago.
I'm trying to use a while loop to iterate through a function in Python which requests a user input, i.e. commitchanges to perform one of two actions, and if the input is not valid after a few attempts to close the program.
If the input is equal to 'y' (Yes) then the program should commit the changes made by the SQL update query, i.e. conn.commit()
If the input is equal to 'n' (No) then the program should not commit the changes and rollback, i.e. conn.rollback()
If the input is anything other than 'y' or 'n' then the program should alert the user that that is not a valid input.
Here's my current function:
def action(inputattempts):
commitchanges = input()
if commitchanges.lower() == 'y':
try:
conn.commit()
print ('Table Updated. No. of records updated:', totalupdates)
except cx_Oracle.DatabaseError as error:
print(error)
elif commitchanges.lower() == 'n':
conn.rollback()
print ('Rollback - No updates made to table')
else:
print ('Not a valid selection - Try again')
print (inputattempts)
Where the variable totalupdates is a global variable defined later on in the program which counts the number of records affected by the curs.execute SQL query.
I'm then calling the above function in a while loop as follows:
inputattempts = 0
while (inputattempts < 4):
inputattempts += 1
action(inputattempts)
print ('FAILED')
Essentially, I'd like the user to have a maximum of 4 attempts at entering a valid input, i.e. 'y' or 'n' before the program aborts if the input is not valid after those attempts. However, if 'y' or 'n' is entered the first time the function is called the program should either commit or rollback and exit the loop.
Currently, I'm having to complete 4 iterations of the while loop regardless of my input. Is there a way I can tweak any of that syntax to exit the loop when the above conditions are met?
As suggested by #rdimaio:
Your action function returns True if the flow was valid; False otherwise (i.e. invalid user input).
def action(inputattempts):
commitchanges = input()
if commitchanges.lower() == 'y':
try:
conn.commit()
print ('Table Updated. No. of records updated:', totalupdates)
except cx_Oracle.DatabaseError as error:
print(error)
return True
elif commitchanges.lower() == 'n':
conn.rollback()
print ('Rollback - No updates made to table')
return True
print ('Not a valid selection - Try again')
print (inputattempts)
return False
Then, you check whether the flow was valid in your while and break when condition is met:
input_attempts = 0
while input_attempts < 4:
input_attempts += 1
if action(input_attempts):
break
if input_attempts == 4:
print ('FAILED')
Just get the user input outside of actions() and pass it as a parameter:
while inputattempts < 4:
inputattempts += 1
commit_changes = input()
action(inputattempts, commit_changes)
Then after the call to action() you can condition on what you need and break out if appropriate.
You can return a value from your function and check over it's returned value to break or not.
def action(inputattempts):
correct_input = True
commitchanges = input()
if commitchanges.lower() == 'y':
try:
conn.commit()
print ('Table Updated. No. of records updated:', totalupdates)
except cx_Oracle.DatabaseError as error:
print(error)
elif commitchanges.lower() == 'n':
conn.rollback()
print ('Rollback - No updates made to table')
else:
print ('Not a valid selection - Try again')
print (inputattempts)
correct_input = False
return correct_input
then change your while loop to break the loop when
inputattempts = 0
while (inputattempts < 4):
inputattempts += 1
if action(inputattempts):
break
print ('FAILED')
Have your function return a flag telling whether or not the input was accepted:
def action(inputattempts):
commitchanges = input()
if commitchanges.lower() == 'y':
try:
conn.commit()
print ('Table Updated. No. of records updated:', totalupdates)
return True # <-- New
except cx_Oracle.DatabaseError as error:
print(error)
elif commitchanges.lower() == 'n':
conn.rollback()
print ('Rollback - No updates made to table')
return True # <-- New
else:
print ('Not a valid selection - Try again')
print (inputattempts)
return False # <-- New
Now in your loop, you can do
inputattempts = 0
while (inputattempts < 4):
inputattempts += 1
if action(inputattempts):
break
print ('FAILED')
where the break simply exists the loop. Also, I imagine that the print of 'FAILED' should only occur if the loop is exited after the 4 attempts, not if the break was reached. This can be achieved using else. Finally, your while loop really only counts, and so a for loop is more appropriate. Consider
for inputattempts in range(4):
if action(inputattempts):
break
else:
print ('FAILED')
I'm writing some python code that works out a person's total pay.
I am able to do this by getting the user to input their pay, however I would like them just to be able to enter their name and then the name is searched in position 0 of a list (Eg. 0,1 0,2 0,2 etc).
I have tried using a tuple but it is not callable and the dictionary and list would not work for me either.
counter = 0
valid = 0
employeelist = [["thomas","2","500"], ["jake","1","750"]]
while True:
while True:
try:
name = str(input("Name:"))
except ValueError:
print("Error")
continue
else:
break
while True:
if name == employeelist[counter,0]:
print(employeelist[counter])
break
valid = 1
elif counter = 3:
print("invalid name")
break
else:
counter = counter + 1
if valid == 1:
break
months = employeelist[counter,1]
pay = employeelist[counter,1]
totalpay = int(months) * int(pay)
Edit:
I do no longer have the code with the dictionary, however I just edited the code from [counter,1] and [0,1] to [counter][1] and is working fine thank you :D
The code below is for your inner loop
employeelist = [["thomas","2","500"], ["jake","1","750"]]
name = ""
while True:
try:
name = input("Name:")
break
except:
print "Error"
position = -1
for i, element in enumerate(employeelist):
if element[0] == name:
position = i
break
if position == -1:
print "Invalid Name"
else:
totalpay = int(employeelist[position][1]) * int(employeelist[position][2])
Your code have multiple bugs. First, valid=1, is set after breaking the loop -meaning valid=1,is never set. You also are checking elif counter = 3 this way, you should rather use two equality signs, like this: elif counter == 3
The error you are getting, that list indices must be integers or slices, not tuple, you are having because you are accessing a multidimensional array the wrong way. Instead of name == employeelist[counter, 0], it should be name == employeelist[counter][0].
Your way of iterating through the array is possible, but its rather simpler using a for loop.
Try this way.
for employees in employeelist:
if name == employees[0]:
print(employee)
valid = 1
break
If it iterated through the hole employeelist, without the if-block running, valid = 1, would never been set.
Working code:
counter = 0
valid = 0
employeelist = [["thomas","2","500"], ["jake","1","750"]]
while True:
while True:
try:
name = str(input("Name: "))
except ValueError:
print("Error")
continue
else:
break
for employees in employeelist:
if name == employees[0]:
print(employees)
valid = 1
break
if valid == 1:
break
months = employeelist[counter][1]
pay = employeelist[counter][2]
totalpay = int(months) * int(pay)
print(totalpay)