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
Related
I want to only allow a user to input a string followed by an integer and separated using a comma and if they do not comply I would like to let them know that they have inputted incorrectly and to let them try again. I am having trouble finding a way to do this, I can make the input require a comma but I do not know how to specify a string and int on each side.
name_age = []
user_input = []
name_age2 = []
ages = []
while user_input != "":
try:
user_input = input("Type 'Name,Age': ")
name_age.append(user_input)
print(name_age)
if "," not in user_input:
raise ValueError
except ValueError:
print("Incorrect try again")
else:
name_age.remove("")
for item in name_age:
name_age2.append(item.split(','))
for pair in name_age2:
for val in pair:
if val.isdigit():
ages.append(int(val))
print(name_age2)
print(ages)
print("Names & Ages: ", name_age2)
print("Total of ages: ", sum(ages))
print("Average of ages: ", sum(ages) / len(ages))
print("Total number of names: ", len(name_age2))
print("End")
you can for example make a function to validate the user input, like this.
Here's an example, it's working, but you have to make some modifications.
name_age = []
user_input = []
name_age2 = []
ages = []
def validate(input: str) -> bool:
decomposed_string: str = input.split(',')
if decomposed_string[1].isdigit() and not decomposed_string[0].isdigit():
return True
return False
while user_input != "":
try:
user_input = input("Type 'Name,Age': ")
name_age.append(user_input) if validate(user_input) else print("Incorrect, try again")
print(name_age)
if "," not in user_input:
raise ValueError
except ValueError:
print("Incorrect try again")
else:
name_age.remove("")
for item in name_age:
name_age2.append(item.split(','))
for pair in name_age2:
for val in pair:
if val.isdigit():
ages.append(int(val))
print(name_age2)
print(ages)
print("Names & Ages: ", name_age2)
print("Total of ages: ", sum(ages))
print("Average of ages: ", sum(ages) / len(ages))
print("Total number of names: ", len(name_age2))
print("End")
Regex patterns suits very well for this kind of simple string parsing. Other solution in complex cases would be to go for some third party library PyParsing or Parsley.
With regex groups and especially named groups are very handy. Here's a simple example:
name_age_pattern = re.compile(r'(?P<name>\w+),(?P<age>\d+)')
def parse_name_age(string):
match = name_age_pattern.match(string)
if not match:
raise ValueError('input string didnt match pattern')
return match.group('name'), int(match.group('age'))
name, age = parse_name_age('Jack,23')
Then some extra hints. To make your code a little bit more pythonic you could use some classes. Here's a sample:
import re
from typing import NamedTuple
class Person(NamedTuple):
name: str
age: int
name_age_pattern = re.compile(r'(?P<name>\w+),(?P<age>\d+)')
#classmethod
def from_string(cls, string):
match = cls.name_age_pattern.match(string)
if not match:
raise ValueError('input string didnt match pattern')
return cls(match.group('name'), int(match.group('age')))
class People(list):
# Inheriting from a list should be made with caution. Overriding
# native functions could result unwanted side effects. We should
# be safe here since we don't do so.
#property
def names(self):
return ', '.join((person.name for person in self))
#property
def total_age(self):
return sum((person.age for person in self))
#property
def avg_age(self):
return self.total_age / len(self)
people = People()
while True:
input_str = input('Please give a name and age separated by a comma.')
if input_str == "":
break
try:
people.append(Person.from_string(input_str))
except ValueError as e:
print(e)
print(people.names)
print(f'sum of ages: {people.total_age}')
print(f'avg of ages: {people.avg_age}')
Here I have used a regex for your condition to validate the input:
import re
name_age = []
user_input = []
name_age2 = []
ages = []
while user_input != "":
try:
user_input = input("Type 'Name,Age': ")
name_age.append(user_input)
print(name_age)
if not re.search('^\w+(\s+\w+)*,\d+', user_input):
raise ValueError
except ValueError:
print("Incorrect try again")
else:
name_age.remove("")
for item in name_age:
name_age2.append(item.split(','))
for pair in name_age2:
for val in pair:
if val.isdigit():
ages.append(int(val))
print(name_age2)
print(ages)
print("Names & Ages: ", name_age2)
print("Total of ages: ", sum(ages))
print("Average of ages: ", sum(ages) / len(ages))
print("Total number of names: ", len(name_age2))
print("End")
So this regex would accept names with spaces example "anosha rehan, 12" but if you're looking for only one word names so use this regex '(\w+)*,\d+'
You can use regex to match the exact pattern you want from users input.
The regex defined below would match:
[a-zA-Z]+ - one or more alphabet (both lower and upper case)
, - literal comma character
\d{1,3} - a range between 1 to 3 digits. $ must end in digit
import re
# input_text_sample = 'john,24'
input_pattern = re.compile(r'[a-zA-Z]+,\d{1,3}$')
correct_input = None
while not correct_input:
user_input = input("Type 'Name,Age': ")
correct_input = input_pattern.match(user_input)
if not correct_input:
print("Incorrect try again")
else:
print(correct_input.string)
I keep getting an error when I run this functions. Everything goes through and then it shows this error. I have tried adding .items() to the end when I print the dictionary and still throws this error.
CLARIFICATION just realized. Not getting any type errors or anything. It prints fine but when doesn't add the second variable to the dictionary. Instead it prints this..
{'Frappe': ('small', function type_of_milk at 0x000002BE2BCD2F78>)}
def order():
ready_to_order = True
while ready_to_order != False:
ordering_q = input(
"""Do you know what you would like to order or do you need to see the menu?
[M]enu or [R]eady to order or [Q]uit: """)
if ordering_q.upper() == "Q":
sys.exit()
elif ordering_q.upper() == "M":
print(Menu())
elif ordering_q.upper() == "R":
ready_to_order = False
else:
print("Please enter valid letters only, try again.")
print(" ")
print(" ")
add_cart = True
while add_cart != False:
order1 = input("What would you like to order?")
if order1.upper() == "Done":
add_cart = False
elif order1 == 'a1':
print("Frappe added to cart")
global total_order
total_order += 3
drink_size()
type_of_milk()
order_dict['Frappe'] = (drink_sizes, type_of_milk)
add_cart = False
print(order_dict)
This line:
order_dict['Frappe'] = (drink_sizes, type_of_milk)
is adding the function type_of_milk to your dict, which is why you see function type_of_milk at 0x000002BE2BCD2F78> when you print the dict out. Maybe you meant to say type_of_milk()?
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)
Whenever I try to display the Firstname with first initial attached to the end, I get an out of string index range error!
def ForeName():
return raw_input("Please enter your Forename: ")
def MiddleName():
return raw_input("please enter your middle name, if none leave blank: ")
def LastName():
return raw_input("Please enter your last name: ")
def Test():
ForeNameT = ForeName()
MiddleNameT = MiddleName()
LastNameT = LastName()
if not MiddleNameT:
first_username = ForeNameT[0:] + LastNameT[0]
elif ForeNameT:
first_username = ForeNameT[0:][0] #i want to display the first name with the initial of the first name attached to the end of the first name.
else:
first_username = ForeNameT[0:] + MiddleNameT[0]
return first_username
print Test()
You can add an argument to Test function by doing def Test(name_method): and then set if to if name_method == 'without_middlename':.
Try to figure out yourself what you would change print Test() to.
I think i know what you are trying to do, try changing your Test function:
def Test():
ForeNameT = ForeName()
MiddleNameT = MiddleName()
LastNameT = LastName()
if not MiddleNameT:
first_username = ForeNameT + LastNameT
else:
first_username = ForeNameT + MiddleNameT + LastNameT
return first_username
notice the changes to the variable names vs. the function names and the return value so print has something to actually print
While working on my program I have run into a problem where the information stored in Menu option 1 is not being transferred to Menu option 2. As you can see it is correctly stored when in menu one. When it returns to go to menu option 2 its like it never went to option 1.
update #1:
some suggestions I've had is to understand scope? from what I can tell the program is not passing the data along to its parent program even though I've typed out return in each of the definitions.
#Must be able to store at least 4 grades
#Each class can have up to 6 tests and 8 hw's
#Weighted 40%*testavg 40% hw average attendance is 20%
#User must be able to input a minimum grade warning
#after each test the your program must calculate the students average and issue warning if necessary
##Define the Modules##
import math
def menu (a): #2nd thing to happen
menuend = 'a'
while menuend not in 'e':
menuend = raw_input("Type anything other then 'e' to continue:\n")
print "What would you like to do ?"
menudo = 0
print "1 - Enter Courses\n2 - Select Course to Edit\n3 - Save File\n4 - Load File\n5 - Exit\n"
menudo = input("Enter Selection:")
if (menudo == 1):
menuchck = 0
menuchck = raw_input("\nYou have entered #1 (y/n)?:\n")
if menuchck in ["Yes","yes","y","Y"]:
x = m1()
else:
print "I'm sorry,",nam,",for the confusion, lets try again\n"
menu()
elif (menudo == 2):
menuchck1 = 0
menuchck1 = raw_input("\nYou have entered #2 (y/n)?:\n")
if menuchck1 in ["Yes","yes","y","Y"]:
x = m2()
else:
print "I'm sorry,",nam,",for the confusion, lets try again\n"
menu()
elif (menudo == 3):
print "Entered 3"
elif (menudo == 4):
print "Entered 4"
else:
print "Anything Else Entered"
def course(): #3rd thing to happen
b = {}
while True:
while True:
print "\n",name,", please enter your courses below ('e' to end):"
coursename = raw_input("Course Name:")
if (coursename == 'e'):
break
will = None
while will not in ('y','n'):
will = raw_input('Ok for this name : %s ? (y/n)' % coursename)
if will=='y':
b[coursename] = {}
print "\n",name,", current course load:\n",b
coursechck = None
while coursechck not in ('y','n'):
coursechck = raw_input("Are your courses correct (y/n)")
if coursechck =='y':
return b
else:
b = {}
print
##Menu Options##
def m1():
a = course()
return a
def m2():
print "Excellent",name,"lets see what courses your enrolled in\n"
print x
return x
###User Input Section###
name = raw_input("Enter Students Name:\n")
a = {}
menu(a)
raw_input("This is the end, my only friend the end")
In your if-elif blocks in the do==1 case, you write m1(), but for the last case, you write x=m1(). You should have the latter everywhere (by typing m1() you only run the function, but do not store the returned x anywhere).
By the way, you can avoid this if-elif confusion using if chck in ["Yes","yes","Y","y"]: