Python - input control - python

I have problem with users input control in one function in Python 3.4.
def input_name (*args):
name_output = ""
name_input = input ("Give first name: ")
if name_input.isalpha() and name_input[0].isupper() == True:
name_output += name_input
return (name_output)
else:
print ("Wrong, do it again")
input_name ()
name = input_name()
print(name.lower())
I am trying to catch users wrong input - so the name must be alphabetical and first letter must be uppercase. In future code I will create users login name with lowercase letters, so I am trying to print users name with small leters for login name. And there is problem.
When I type name firs time well, it's ok
When I type first time name with 1 lowercase letter (name) and then I write it correctly (Name), it tells me Error, I don't understand why. Can you tell me, what is my mistake?
Thank you very much for showing the path.
Mirek

The error is caused by the last line. Since your input is wrong the first time, the function returns None, so name.lower() raises an exception. I wouldn't use recursion in this case.
def input_name():
while True:
name_input = input ("Give first name: ")
if name_input.isalpha() and name_input[0].isupper():
return name_input
else:
print ("Wrong, do it again")
name = input_name()
print(name.lower())
Hope it helps!

Related

Python infinite while loop issue

I am trying to make a script that asks for user input in Python, it is supposed to error with the response "Please enter first name", and then return the user back to the initial input question prompt.
This isn't working, instead after asking for both the first and last name if no name is given for both I am thrown into an infinite loop of the first error.
# User input for first name
first_name = input('Enter FIRST name here: ')
# User input for last name
last_name = input('Enter LAST name here: ')
def print_name():
# store user input in separate variable
fname = first_name
lname= last_name
while True:
# throw error if user enters no first name
if len(fname) == 0:
# error msg
print('No FIRST name entered...')
# loop back to prompt asking for first name
continue
else:
# if first name given move on to prompting for last name
# break loop
break
# loop into prompting user for last name
while True:
# throw error if user enters no last name
if len(lname) == 0:
print('No LAST name entered...')
# loop back to prompt asking for last name
continue
else:
# if last name given move on to running print command
# break loop
break
return fname, lname
print(f'your name is {fname} {lname}')
print_name()
Please can someone help me understand whats going wrong here? It should only loop back to asking for a first name (or last name) when nothing is given, other wise it should print the users name to console. both names should be given too, if first name is not given then id expect an error in the first while loop, like wise if last name is not given.
Also is there a better way to do this? using 2 while loops seems wrong?
Don't repeat yourself. If you copy and paste a section of code, stop and think. It should either be a function, or a loop.
def wait_for_input(prompt):
data = ""
while data == "":
data = input(prompt).strip()
return data
def print_name(fname, lname):
print(f'your name is {fname} {lname}')
first_name = wait_for_input('Enter FIRST name: ')
last_name = wait_for_input('Enter LAST name: ')
print_name(first_name, last_name)
Also, don't use comments to repeat what the code says.
The issue is with your infinite loops, you can simplify your function like:
def print_name():
first_name = ""
last_name = ""
# User input for first name
while first_name == "":
first_name = input('Enter FIRST name here: ')
# User input for last name
while last_name == "":
last_name = input('Enter LAST name here: ')
print(f'your name is {first_name} {last_name}')
I have the impression you are new at this:
While-loops generally look as follows:
while <condition>
...
<check_condition>
...
This means that in most cases, at every time the loop is executed, the condition is re-calculated and checked again by the while.
In your case, this would become something like:
while (len(fname) == 0)
<show_error_message>
<get fname again>
The case you have written here (while true) also exists and is used regularly, but in very different cases, like in multi-threaded event-based programs:
while true
<get_event>
This means that a part of the program (a so-called thread) is waiting for an event (like a buttonclick) to be catched and then something happens. This, however, is mostly done in multi-threaded applications, which means that the "main" program is doing something, while a subprogram is handling the events, which are coming in.
I am not fully understanding why you need so many loops. Something like this should do:
def print_name():
fname = input('Enter FIRST name here: ')
if len(fname) == 0:
raise Exception('No FIRST name entered...')
lname= input('Enter LAST name here: ')
if len(lname) == 0:
raise Exception('No LAST name entered...')
print(f"your name is {fname} {lname}")
And if all you wanted is to repeat this loop all you need to do is nest your print_name() function in a loop.
EDIT: Now that I seen other answers, I believe #Tomalak answer is better, was not getting what you really wanted.
Try this code:
def print_name():
# store user input in separate variable
first_name = input('Enter FIRST name here: ')
fname = first_name
while True:
fname = first_name
# throw error if user enters no first name
if len(fname) == 0:
# error msg
print('No FIRST name entered...')
first_name = input('Enter FIRST name here: ')
# loop back to prompt asking for first name
continue
else:
# if first name given move on to prompting for last name
# break loop
break
# loop into prompting user for last name
while True:
last_name = input('Enter LAST name here: ')
lname= last_name
# throw error if user enters no last name
if len(lname) == 0:
print('No LAST name entered...')
# loop back to prompt asking for last name
continue
else:
# if last name given move on to running print command
# break loop
break
return fname, lname
print(f'your name is {fname} {lname}')
print_name()

My function drops me in the return a "none" when I thought I placed a string?

I have two files and I want to store in Username.txt, but my function just drops me a "None".
def username_input(user):
user_len = int(len(user))
while user_len > 12 or not (user.isalnum()):
print("Error: the username must be an alphanumeric value \nand have as maximum 12 characters")
user = input("Type a valid username: ")
user_len = int(len(user))
if user_len <= 12 and user.isalnum():
return user
with open("Username.txt", "a") as usr_txt:
usr = username_input(user = input("Type a username: "))
usr_txt.write(usr)
In python, no return or return means return None. If never enter the while user_len > 12 or not (user.isalnum()):, it will reach end and no return here, so it will get None from outside, when you call username_input.
Hope that will help you, and comment if you have further questions. : )
As #Michael Butscher said, if the username you pass the first time doesn't meet the while condition you're giving None back. To fix that you can remove the inner if check and return the username before leaving your function:
def username_input(user):
while len(user) > 12 or not (user.isalnum()):
print("Error: the username must be an alphanumeric value \nand have as maximum 12 characters")
user = input("Type a valid username: ")
return user
If I understand correctly, you want to ask the user for a username and then store their input in a file called Username.txt if it matches certain criteria.
The reason your code returns None at the moment is due to the conditions of the while loop not being satisfied. Because there is no default return statement after the while loop, if the while loop's conditions are not met, the function will exit and return None.
I suggest refactoring your code slightly:
def username_input():
while True:
user = input("Type a valid username: ")
if len(user) <= 12 and user.isalnum():
return user
else:
print("Error: the username must be an alphanumeric value \nand have as maximum 12 characters")
with open("Username.txt", "a") as usr_txt:
usr = username_input()
usr_txt.write(usr)
As a side note, this programming construct is called "loop-and-a-half". It is Python's equivalent of the "do-while" loop. See here for more information.

Error while getting input added into list

I am attempting to create a program that asks for the user to enter his/her name and records the input into a list (Was working towards dictionary but seems like I made a boo boo!) but it is returning with "TypeError: can only concatenate list (not "str") to list". The following is the code.Thanks in advance.
namedic = []
while True:
print ("Please, enter your name:")
name = input()
if len(name) > 3:
print ("Welcome")
else:
print ("Ew, your name have less than 4 letters! Gross! Try a new one")
continue
namedic = namedic + name
print ("Ah, your name have at least 4 words, good name.")
for name in namedic:
print (name)
Your erroring line is namedic = namedic + name. What you're trying to do is add a list (namedic) to a string (name). You should do namedic.append(name) instead.
The + operator isn't used to append elements to a list, as the error shows. You can use the append method for that:
namedic.append(name)
#your code should rather be like this;
namedic = []
while True:
print ("Please, enter your name:")
name = input()
if len(name) > 3:
print ("Welcome")
else:
print ("Ew, your name have less than 4 letters! Gross! Try a new one")
continue
namedic.append(name)
print ("Ah, your name have at least 4 words, good name.")
for name in namedic:
print (name)

I'm having issues looping through a tuple

I'm writing a program to simulate a bank scenario. It allows users (once they have logged in) to display their balance, withdraw and deposit money, and will eventually contain a transfer subroutine.
My problem is the login subroutine. I am having trouble getting my code to loop through both accounts in the tuple that saves them. I can log in to the first account and perform all the code's functions with that account but when I try to access the second account, I get the error message telling me that the account is not found. I am very certain that this is to do with the way I have written my loop but I cannot figure out how to fix it.
If someone could provide me with a solution that I can use to fix this error then I would be very grateful. If it is possible, could the solution also stick to the general format of my code and not be one where I have to create new functions and change the entire body of my LogOn function?
Here is the code:
accounts=[
["Luke",'00001','1234',1337],
["Louis",'00002','4321',420],
]
name = ""
x = []
def LogOn():
global name
global x
print ("Welcome to the Bank of Bailey")
accnum = raw_input("-please enter your account number-")
if len(accnum) != 5:
print("That number is not valid, please enter a valid number")
LogOn()
else:
for x in range(0,len(accounts)):
if accnum in accounts[x][1]:
name = accounts[x][0]
print ("Account found")
pin = raw_input("-please enter your pin-")
if pin in accounts[x]:
print ("Pin found")
MainMenu()
else:
print("Pin not found")
print("Please try again")
LogOn()
break
else:
print("Account not found")
print("Please try again")
LogOn()
The problem is:
if accnum in accounts[x][1]:
do something
else:
print("Account not found")
print("Please try again")
LogOn()
The else branch is called already for the first iteration (with x = 0)!
You should check after the while-loop if an account was found and otherwise print your error message.
Instead of recursing to LogOn() you could better use an outer (endless-)while-loop which calls LogOn(). In LogOn then just do a return in error case.
A few observations:
The else branch is called on the first iteration when x=0. You need to finish the for loop, then branch if the account has not been found.
if pin in accounts[x] is a security hole because it allows people to use their name or account number as their pin!
"if accnum in account[1]" should be "if accnum == account[1]"
Hope that helps!
accounts=[
["Luke",'00001','1234',1337],
["Louis",'00002','4321',420],
]
name = ""
x = []
def LogOn():
global name
global x
print ("Welcome to the Bank of Bailey")
accnum = raw_input("-please enter your account number-")
if len(accnum) != 5:
print("That number is not valid, please enter a valid number")
LogOn()
else:
found = False
for account in accounts:
if accnum == account[1]:
found = True
name = account[0]
print ("Account found")
pin = raw_input("-please enter your pin-")
if pin in account[2]:
print ("Pin found")
MainMenu()
else:
print("Pin not found")
print("Please try again")
LogOn()
break
if not found:
print("Account not found")
print("Please try again")
LogOn()
I like #TimSC's answer, but I'll add a few more observations:
LogOn is not a good function name; it should be log_on to match PEP8 naming conventions (LogOn suggests it is a class rather than a function).
You declare name and x as globals, then never use them. Globals are almost always a bad idea, and unused declarations just clutter your code.
As #CTX pointed out, you are using recursion (LogOn calling LogOn calling MainMenu calling LogOn etc) where iteration would make a lot more sense - you should back out on an error, not call again a level deeper.
Mixing input/output code and action code is usually a bad decision; it makes your code less flexible and reusable and often harder to debug as well. You should instead have an input/output function which calls an action function with the values it needs.
Giving separate error messages for bad account number, bad pin number is a security hole; it makes it much easier for would-be attackers to find valid account numbers to attack.
Looking things up in a list is relatively slow (O(n) ie time proportional to the number of items in the list). Use a dict instead (O(1) ie constant time - a quick hash calculation takes you right to the item you are looking for).
Here is a fancied-up version; may it give you things to think about:
import sys
# version compatibility shim
if sys.hexversion < 0x3000000:
inp = raw_input # Python 2.x
else:
inp = input # Python 3.x
class Account:
index = {}
__slots__ = ("accnum", "pin", "name", "balance")
#classmethod
def log_in(cls, accnum, pin):
key = (accnum, pin)
acc = Account.index.get(key, None)
if acc is None:
raise ValueError("No such login (bad account# or PIN)")
else:
return acc
def __init__(self, accnum, pin, name, balance):
# save values
self.accnum = accnum
self.pin = pin
self.name = name
self.balance = balance
# add self to account index
key = (accnum, pin)
Account.index[key] = self
# create accounts
Account('00001', '1234', "Luke", 1337)
Account('00002', '4321', "Louis", 420)
def main():
while True:
print("\nWelcome to the Bank of Bailey")
accnum = inp("Please enter your account number: ").strip()
pin = inp("Please enter your PIN: ").strip()
try:
acc = Account.log_in(accnum, pin)
print("\nYour account has a current balance of ${:0.2f}".format(acc.balance))
print("Thank you for banking with us!")
except ValueError as ve:
print(ve)
if __name__ == "__main__":
main()

How can I tell the difference between a user-input blank string and one I created?

The task is to get a user to input a password then, using recursion, make sure it has no vowels in it. If it does then let the user re-enter the password. This is what i have so far:
def passwordCheck(pwd):
"""checks if pwd has any vowels in it."""#doc string
vowels = 'aeiou'#specifies the characters that aren't allowed
if pwd == '':
return 0
elif pwd == None:
return None#Shouldn't be necessary but just in case
elif pwd[0] not in vowels:#checks that the 1st(0th) character is not a vowel
return passwordCheck(pwd[1:])#gets rid of the 1st(0th) character and starts again
elif pwd[0] in vowels:#checks if the 1st(0th) character is a vowel
return 1#if it is, stops the function calls and returns a value
password = str(input('Please enter a password with no vowels in it: '))#asks user to input their new password
x = passwordCheck(password)#checks the password is valid, i.e. no vowels
while x == 1:#when the password entered contains a vowel
print('\nSorry, that is not a valid password.\nYour password cannot contain any vowels.')#tells the user why their password is invalid
password = str(input('\nPlease enter a different password: '))#gives the user a chance to re-enter their password
x = passwordCheck(password)#checks to make sure the new password is valid
print('\nCongratulations, you have entered a valid password!')#tells the user if their desired password is valid
print('\nYou are now able to log on to the system with these credentials.')#could've been included on the previous line but looks neater here
I know this is probably not the most pythonic way of doing it but it works for me in most cases. I'd love to hear a better way but ideally someone can help in the same style. I don't want to just copy someones code without understanding it.
The question i have is dealing with the case where the user enters no password at all. The first if statement:
if pwd == '':
return 0
I thought it just dealt with the case when the string had been fully recursed through, i.e. no vowels, but after a minutes inspection it's obvious this applies to no password as well.
I had also tried using:
if pwd == None:
return something
Now i'm thinking the problem could be because i said:
password = str(input('######'))
but i've fiddled with that as well and still can't can't seem to make that work either! I've tried google and searching stackoverflow but no luck so if anyone has any ideas/solution they think might be helpful I'd be very grateful to hear them. Thank you very much.
My main question is:
How can i differentiate between a string that's empty because it's been recursed through and the user inputting nothing?
Solved.
ended up using
def passwordValid(pwd):
if len(pwd)>0 and passwordCheck(pwd)==0:
return pwd
else: return 'Fail'
password = str(input('Please enter a password with no vowels in it: '))#asks user to input their new password
y = passwordValid(password)#checks the password is valid, i.e. no vowels
while y == 'Fail':#when the password entered contains a vowel
print('\nSorry, that is not a valid password.\nYour password cannot contain any vowels or be empty.')#tells the user why their password is invalid
password = str(input('\nPlease enter a different password: '))#gives the user a chance to re-enter their password
y = passwordValid(password)#checks to make sure the new password is valid
print('\nCongratulations, you have entered a valid password!')#tells the user if their desired password is valid
print('\nYou are now able to log on to the system with these credentials.')#could've been included on the previous line but looks neater here
Thank you Wayne Werner for fixing the title and the main question.
This problem can be broken down into (at least) three distinct subproblems:
check whether a string contains vowels
check whether a string is a valid password (length > X and has vowels)
get a password from the user
Your code should reflect this structure. You could therefore use the following function layout:
def has_vowels(string):
if not string: # check for empty string
return False # empty strings never have vowels
# TODO we have a non-empty string at this point and can use recursion
def is_valid_password(string):
return len(string) > 0 and not has_vowels(string)
def request_password():
while True: # use an endless loop here, we don't won't to repeat
# the "input" statement. We could also change this to
# something like `for i in range(3)` to allow only a limited
# number of tries.
passwd = input('Please enter a password with no vowels in it: ')
# TODO check if input is valid, if yes, return, if no, print an error
Don't attempt to solve both problems with a single method. You have two ditinct critera: no vowels; minimum length.
def isPasswordValid(pwd):
return len(pwd) > 4 and not passwordCheck(password)
x = isPasswordValid(password)
...
You could solve this with recursion by adding another parameter which indicates how many characters have been looped through, but that is clumsy and offers no real benefit.
You can't differentiate between an empty string and an empty string. You can however set the variable to None, or to a string like "__no_string_entered_yet". That said, I don't see why you would need to, see the other answers.
I believe this does what your question asks for:
Not allow an empty password (different than a "blank" password?)
Not allow vowels in the password
I opted not use if/elif/else in favor of structuring it so that valid characters "fall through"
def pwd_check(s):
vowels = 'aeiou'
if len(s) == 0: return False # This is only valid in the first iteration
if s[0] in vowels: return False
if len(s) == 1: return True # Success: a 1 character pwd with no vowels
return pwd_check(s[1:])
I thought about putting checks in to make sure that a string like ' ' was not passed in, but I didn't see that explicitly asked for. pwd_check(password.strip()) solves this problem.
Here's how I like to do.
For the fun, I added conditions of minimum and maximum lengths for the password:
def passwordCheck(pwd,vowels = 'aeiou',minimum=5,maximum=12):
if pwd == '':
return 0,None,None
elif pwd[0] in vowels:
return -1,None,None
else:
y = passwordCheck(pwd[1:])[0]
if y==-1:
return -1,None,None
else:
return y + 1,minimum,maximum
mess = 'Please enter a password with no vowels in it: '
while True:
x,miin,maax = passwordCheckstr(input(mess))
if x==-1:
mess = ('\nSorry, that is not a valid password.\n'
'Your password cannot contain any vowels.\n'
'Please enter a different password: ')
elif x==0:
mess = ('\nSorry, you must enter a password.\n'
'Please do enter a password: ')
elif x<miin:
mess = ('\nSorry, the password must have at least %d characters.\n'
'The string you entered has %d characters.\n'
'Please, enter a new longer password: ' % (miin,x))
elif x>maax:
mess = ('\nSorry, the password must have at most %d characters.\n'
'The string you entered has %d characters.\n'
'Please, enter a new shorter password: ' % (maax,x))
else:
print ('\nCongratulations, you have entered a valid password!\n'
'\nYou are now able to log on to the system with these '
'credentials.')
break
edit
Another kind of algorithm.
I wasn't satisfied to return such tuple as -1,None,None
def check_password(forbidden,minimum,maximum):
def passwordCheck(pwd,cnt=0,forbid = forbidden,
miin=minimum,maax = maximum):
# cnt is the number of preceding turns of recursion
# that have been already executed.
if pwd == '':
if cnt==0:
# cnt==0 means that it's the first turn of recursion
# since pwd is '', it means no entry has been done
return 0
elif cnt<miin:
return -3
elif cnt>maax:
return -2
elif pwd[0] in forbid:
return -1
else:
if cnt in (-3,-2,-1):
return cnt
else:
return passwordCheck( pwd[1:] , cnt+1 )
mess = 'Please enter a password with no vowels in it: '
while True:
x = str(raw_input(mess)).strip()
y = passwordCheck(x)
if y==0: # inexistent string
mess = ('\nSorry, you must enter a password.\n'
'Please do enter a password: ')
elif y==-1: # string contains a vowel
mess = ('\nSorry, that is not a valid password.\n'
'Your password cannot contain any vowels.\n'
'Please enter a different password: ')
elif y==-2: # string too long
mess = ('\nSorry, the password must have at most %d characters.\n'
'The string you entered has %d characters.\n'
'Please, enter a new shorter password: ' % (maximum,len(x)))
elif y==-3: # string too short
mess = ('\nSorry, the password must have at least %d characters.\n'
'The string you entered has %d characters.\n'
'Please, enter a new longer password: ' % (minimum,len(x)))
else: # success
print ('\nCongratulations, you have entered a valid password!\n'
'You are now able to log on to the system with these credentials.')
break
# EXECUTION
check_password('aeiou',5,12)

Categories