Add a attempt counter to while loop via subtracting variables - python

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.

Related

Why is my while loop being ignored inside of this function?

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.

How can I check if a string has personalized errors?

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

For, if, else statements

So I have a multilayered problem. I am trying to I am trying to just print out 'input was jack' if the raw_input was 'jack' and also subtract 'tries' by 1, and then run the function again and print out amount of 'tries' at the end of both the first 'if' statement and the 'else' statement, and if the raw_input wasn't 'jack' it adds 1 to 'tries' and if 'tries' reaches a total of 4 the function prints stop. I have run into a couple of different problems. Either the counter seems to reset every single time so it's always just '1' or I can't just keep running the function. It just doesn't seem like it's following the 'if or else' theory I thought it would be. So I have the following code:
tries = 0
def usrinfo():
name = raw_input('input name ')
a = ('jill','jack',name,'meg')
for element in a:
if element == 'jack':
print('input was jack')
tries =-1
print(tries)
usrinfo()
else:
if element != 'jack':
tries =+ 1
return tries
print(tries)
usrinfo()
if tries == 4:
print('stopped')
usrinfo()
If I type 'jack' I get:
Nothing...as in a blank output
I want:
input was jack
-1
Then I want the function to run again...and get
input was jack
-2
And so on.
If I type anything other than 'jack' I get:
Nothing...as in a blank output
I want to get:
1
Then for the function to run again....and get
2
Then if I type anything other than 'jack'. I want with each attempt to print the increasing level of tries from 1 - 4 such as below:
1
2
3
4
Then when it reaches 4 it prints 'stopped' or runs whatever command. I want to essentially keep the code running and control the value of 'tries' according to whether or not the input is 'jack' or something else. Because even when I create an even simpler code like this:
def userinput():
tries = 0
username = raw_input('input username ')
if username == 'jack':
tries -=1
print(username + ' '+ str(tries) + ' success')
userinput()
else:
if username != 'jack':
tries +=1
print('tries ' + str(tries))
userinput()
userinput()
My results on this code with input of 'f' are:
input username f
tries 1
input username f
tries 1
input username f
Instead I want to get:
input username f
tries 1
input username f
tries 2
input username f
tries 3
And with input of 'jack' I want:
input username jack
jack -1 success
input username jack
jack -2 success
input username jack
jack -3 success
input username jack
jack -4 success
And so on. Any help is much appreciated. I'm not sure what I'm doing wrong. It just seems like 'tries' is constantly resetting itself.
Hopefully this is clear...I've spent a lot of time trying to make code just to be able to demonstrate to someone with more knowledge where my misunderstanding might be...
Thanks guys and gals.
Here's what I came up with, no loops required
#!python3
from os import sys
def usrinfo(tries):
name = input('input name: ')
if tries == 4:
print('end of program because tries equals 4')
sys.exit()
elif name == 'jack':
tries -= 1
print('input was jack')
print('tries ' + str(tries) + '\n')
usrinfo(tries)
elif name != 'jack':
tries += 1
print('tries ' + str(tries) + '\n')
usrinfo(tries)
usrinfo(0) # 'tries' will be initialized to zero
When you are taking user input it is stored in the name variable so you need to do if with 'name' not 'element'. Secondly call the function after you have checked the condition
if tries == 4:
print('stopped')
return
usrinfo()
Here is the correct code:
tries = 0
def usrinfo():
global tries
name = raw_input('input name ')
a = ('jill','jack','meg')
#to check the input against the strings of list a go for
#if name in a:
if name == 'jack':
print('input was jack')
tries =-1
print(tries)
usrinfo()
else:
tries+=1
print(tries)
if tries == 4:
print('stopped')
return
usrinfo()
usrinfo()

Look up salary information by name from a list

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)

How can i make this program faster(more efficient)?

I have this code that creates unique passwords using the first letter of each word from the file.Before each password is created(written to a file) it is compared to all passwords that are currently in the file, so if the file has 50,000 passwords before a another is written it has to scan through all 50k.
A user can input any amount of passwords needed but the bigger the number the longer it takes 100k will take a long time how can i optimize this to make the program run faster ? The password generation is not included code
for mainLoop in range(passnum):
user = 0
newpass = generatePassword() # New password generated each iteration of loop
storePass = open("MnemPass.txt","a")
print ("PASSWORD GENERATED ")
#Checks if file is empty if True write first password
fileEmpty = os.stat("MnemPass.txt").st_size == 0
if fileEmpty == True:
storePass.write(newpass)
storePass.write("\n")
storePass.close()
print ("FIRST PASSWORD WRITTEN")
#IF file is not empty Read line by line and compare each with new password generated returns boolean value
elif fileEmpty == False:
storePass = open("MnemPass.txt","r")
with open("MnemPass.txt") as f:
fileData = f.read().splitlines()
linelength = len(fileData).__int__()
filemax = linelength
num = linelength #Number used to cycle through array set to size of list
#print(linelength+10)
for iterate in range(linelength):
num = num - 1 #Number decreases each pass
#print num
if fileData[num] != newpass: # The last element in the array is checked first decrementing each pass
go = True
if fileData[num]==newpass: #changed
print ("match found: PASSWORD : "+fileData[num])
passMatch = open("Matchpassword.txt","a")
passMatch.write(newpass)
passMatch.write("\n")
passMatch.close()
go = False
break
#places new password once it does not match and all elements prev passwords are compared
if go == True and num == 0:
storePass = open("MnemPass.txt","a")
storePass.write(newpass)
storePass.write("\n")
storePass.close()
print ("NEW WRITTEN")
if linelength == filemax:
num = num -1
*new Code - i used the set function *
passnum = (input("How many passwords do you need :"))
sTime = datetime.now()
storePass = open ("MnemPass.txt","a") # file open out of loop to increase speed
fileEmpty = os.stat("MnemPass.txt").st_size == 0
new_passwords = set()
CurrentPasswords = set()
if fileEmpty == True:
while len(new_passwords)!= passnum: #will cause problems if dictionary cannot create amount needed
new_passwords.add(generatePassword())
for pw in new_passwords:
storePass.write(pw + "\n")
else:
new_passwords = set(line.strip() for line in open ("MnemPass.txt"))
for num in range(passnum):
temp = generatePassword()
if temp not in new_passwords:
CurrentPasswords.add(temp)
else:
"match found"
for pw2 in CurrentPasswords:
storePass.write(pw2 + "\n")
You can considerably reduce runtime by loading the file once and then appending each new password to it rather than open file in loop and check line by line,Here I am using uuid in generatePassword() to generate a random string of length between 3 and 10
Your code:
def func(passnum):
import os,uuid,random
def generatePassword():
return str(uuid.uuid4()).replace('-', '')[0:random.randint(3,10)]
for mainLoop in range(passnum):
user = 0
newpass = generatePassword() # New password generated each iteration of loop
storePass = open("MnemPass.txt","a")
print ("PASSWORD GENERATED ")
#Checks if file is empty if True write first password
fileEmpty = os.stat("MnemPass.txt").st_size == 0
if fileEmpty == True:
storePass.write(newpass)
storePass.write("\n")
storePass.close()
print ("FIRST PASSWORD WRITTEN")
#IF file is not empty Read line by line and compare each with new password generated returns boolean value
elif fileEmpty == False:
storePass = open("MnemPass.txt","r")
with open("MnemPass.txt") as f:
fileData = f.read().splitlines()
linelength = len(fileData).__int__()
filemax = linelength
num = linelength #Number used to cycle through array set to size of list
#print(linelength+10)
for iterate in range(linelength):
num = num - 1 #Number decreases each pass
#print num
if fileData[num] != newpass: # The last element in the array is checked first decrementing each pass
go = True
if fileData[num]==newpass: #changed
print ("match found: PASSWORD : "+fileData[num])
passMatch = open("Matchpassword.txt","a")
passMatch.write(newpass)
passMatch.write("\n")
passMatch.close()
go = False
break
#places new password once it does not match and all elements prev passwords are compared
if go == True and num == 0:
storePass = open("MnemPass.txt","a")
storePass.write(newpass)
storePass.write("\n")
storePass.close()
print ("NEW WRITTEN")
if linelength == filemax:
num = num -1
I slightly modified it to load the file at starting itself and append for every new password, notice that we don't need inner for loop anymore, the code becomes:
def func2(passnum):
import uuid
import os, random
linelength = 0
fileData = []
if os.path.isfile('MnemPass.txt'):
f = open("MnemPass.txt", "r")
fileData += f.read().splitlines()
linelength = len(fileData).__int__()
f.close()
def generatePassword():
return str(uuid.uuid4()).replace('-', '')[0:random.randint(3,10)]
for mainLoop in range(passnum):
user = 0
newpass = generatePassword() # New password generated each iteration of loop
storePass = open("MnemPass.txt", "a")
print ("PASSWORD GENERATED ")
# Checks if file is empty if True write first password
fileEmpty = os.stat("MnemPass.txt").st_size == 0
if fileEmpty == True:
storePass.write(newpass)
storePass.write("\n")
storePass.close()
print ("FIRST PASSWORD WRITTEN")
# IF file is not empty Read line by line and compare each with new password generated returns boolean value
elif fileEmpty == False:
storePass = open("MnemPass.txt", "r")
filemax = linelength
num = linelength # Number used to cycle through array set to size of list
# print(linelength+10)
if newpass in fileData:
print ("match found: PASSWORD : " , fileData.index(newpass))
passMatch = open("Matchpassword.txt", "a")
passMatch.write(newpass)
passMatch.write("\n")
passMatch.close()
else:
linelength += 1
fileData.append(newpass)
storePass = open("MnemPass.txt", "a")
storePass.write(newpass)
storePass.write("\n")
storePass.close()
print ("NEW WRITTEN")
if linelength == filemax:
num = num - 1
Profile for your code:
Profile for modified code:
As you can see the runtime has reduced from 45secs to 27secs ! :)
NOTE:
I ran the tests for 10000 passwords and deleted the generated files for next pass :)
In the case that your code runs in a loop as you present here:
Checking the whole file on every loop iteration is not very efficient.
It is much more efficient to keep a set of created passwords and check if a new password is already in the set before adding it.
Also in this case you should only open the file once outside the main for loop and close it afterwards.
In the case that your program adds only one password and then returns, it is better to add each new password in a way that your file remains sorted. This way you can use binary search to search if a password already exists.

Categories