Curious about adding classes and how to add them - python

So basically I'm just trying to make a simple coding thing in python that incorporates classes, user input user output, conditionals, loops and arrays and so far I have everything added but for classes and loops. And im sort of lost on how I would add a class to this code ive looked around on how to do classes but im still a little confused. here is my code for my current program. a quick little summary about it is that its basically just a barbershop and depending on the inputs and the lists it will give you a certain haircutl. I know its a little simple but I just started programming not to long ago.
print("Welcome to Sids Barbershop")
Question = input("Would you like to get your haircut today")
if(Question == "yes"):
print ("Great come in and take a seat")
elif (Question == "no"):
print("Sounds good come back when your ready")
mylist = [['boy','14','curly'],
['girl','12','straight'],
['Man','28','wavy'],
['Woman','26''curly']]
record = []
gender = input("Do you consider yourself a boy,girl, man or woman")
record.append(gender)
age = input("How old are you")
record.append(age)
hair = input("what kind of hair type do you have")
record.append(hair)
search = int(input('Select and index to search an entry:'))
print(mylist[search])
if (search == 0):
print("alright so far so good would you like to get a fade and your hair cut by 2 inches?")
if(search == 1):
print("Ok so you have straight hair would you just like a trim?")
if(search == 2):
print("So you have wavy hair which is hard to deal with do you want to go bald?")
if(search == 3):
print('ok you have super long and curly hair do you want to try something new and perm your hair straight?')
i have tried to look up what a class was and how to incorporate it but I'm not to sure on how to.

A good use case for a class definition here would be the "customer" object that you're currently using the list class for. You can simplify the class definition with the #dataclass decorator:
from dataclasses import dataclass
#dataclass
class Customer:
gender: str
age: str
hair: str
Then replace the lists in mylist with Customers:
mylist = [
Customer('boy','14','curly'),
Customer('girl','12','straight'),
Customer('Man','28','wavy'),
Customer('Woman','26','curly'),
]
and define record as a Customer like this:
record = Customer(
input("Do you consider yourself a boy,girl, man or woman"),
input("How old are you"),
input("what kind of hair type do you have"),
)
Whereas with a list you can only access elements by their numeric index, your new Customer class has named attributes -- instead of record[1] to get the age the user entered, you can do record.age, etc.

Related

IndexError: list index out of range shows up when the value specified is inside the range of the list

I get "IndexError: list index out of range" when I try to run this, even though the random choice made is within the list range. I want the computer to make a choice and perform a set of action that I have made. I can't seem to identify the issue in this code. I've inserted the code down below.(also I did import random)
def do_question():
questions = ["What is your favourite movie?", "What is your name?", "What are your hobbies?"]
q_choice = random.choice(questions)
if q_choice == questions[0]:
inter = input(f"{q_choice}")
questions.remove(q_choice)
print("Amazing! My favourite movie is Pirates Of The Caribbean: The Curse Of The Black Pearl")
if q_choice == questions[1]:
inter = input(f"{q_choice}")
questions.remove(q_choice)
print(f"Glad to talk to you, {inter}. I'm PYchatter.")
if q_choice == questions[2]:
inter = input(f"{q_choice}")
questions.remove(q_choice)
print("Wow! Those are nice hobbies you have. My hobby is talking to people like I am doing right now.")
You are calling questions.remove then at next if statement questions array items.
In sample code you have given you initialize list with 3 items, consider first if statement is true so you remove one item from list that results in array with 2 items, then at third if statement you are checking equality with third item that does not exist so far and throws exception.
One solution is using if-else statement.
What's happening here as #Eskandar answer explained, is that you're changing the length of the list and then trying to access it again as if it had the same length.
Consider this:
l = [1, 2, 3]
choice = random.choice(l) # Let's say that the choice is 2
if choice == l[0]: # Skipped
print("Choice was 1")
l.remove(choice)
if choice == l[1]:
print("Choice was 2")
l.remove(choice) # Since choice was 2, now the list l is [1, 3]
if choice == l[2]: # But there are only 2 elements in l!!!
...
A better way to do what you're trying to do could be:
import random
qa_map = {
"What is your favourite movie?": "Amazing! My favourite movie is Pirates Of The Caribbean: The Curse Of The Black Pearl",
"What is your name?": "Glad to talk to you, {}. I'm PYchatter.",
"What are your hobbies?": "Wow! Those are nice hobbies you have. My hobby is talking to people like I am doing right now.",
}
def do_question() -> None:
q_choice = random.choice(list(qa_map.keys())) # Choose a random question
answer = input(q_choice) # Ask the question
# Get the answer from the map and format it. If the reply doesn't have any
# formatting, it won't do anything, which is what we want.
reply = qa_map[q_choice].format(answer)
print(reply)
# Remove the question from the map so it can't be asked again
del qa_map[q_choice]
Now if I do do_question() three times and the input I give is always "apples" I get:
Amazing! My favourite movie is Pirates Of The Caribbean: The Curse Of The Black Pearl
Glad to talk to you, apples. I'm PYchatter.
Wow! Those are nice hobbies you have. My hobby is talking to people like I am doing right now
.

allowing user to select from a list using the index location

My apologies up front if I haven't asked this question correctly, it is my first time asking here.
I have a script that works currently, but I am trying to improve it.
I create a dictionary of days of the week and against each value I allow the user to input which meal they would like to eat on that day. However, at present I require the use to type their selected option from a list, which must match exactly. for instance "Szechuan pork stir-fry" - it is easy to make a typo here. Rather than manage the typo i'd like to make it easier for the use to make their selection from the list e.g. by selecting a key from a dictionary or an index location from the list - I am having trouble getting either of those to work though.
my code looks like this right now:
for d in week_meals:
try:
answer = input(f"What would you like to eat on {d}? options are {week_meals_list}")
week_meals_list.remove(answer)
week_meals[d] = answer
except ValueError:
print("That isn't an option!")
answer = input(f"What would you like to eat on {d}? options are {week_meals_list} type {week_meals_list.index")
week_meals_list.remove(answer)
week_meals[d] = answer
I have tried to create a dictionary by doing something like the below but I cannot figure out how to set the key for each item to increment up by 1:
week_meals_dict = {}
for k in range(int(days)):
week_meals_dict[k] = None
but then I really cannot figure out a way to iterate through each key while simultaneously iterating through a list in parallel. is this even possible?
this got me thinking it might be easier to just ask the user to input the index location within the list but I can't figure that out either.
I figured it out...
not really sure I fully understand why it works but this does seem to:
for k in range(int(days)):
week_meals_dict[k] = week_meals_list[k]
I can maybe even clean this up but I am happy with it for now
week_meals = {}
week_meals_list = ['meal1', 'meal2',]
for d in range(1, 8):
meal = None
while not meal:
answer = input(f"What would you like to eat on {d}? options are {week_meals_list}")
try:
meal = week_meals_list[int(answer)]
week_meals_list.remove(meal)
week_meals[d] = meal
except (IndexError, ValueError):
pass
First of all, don't modify a list during iteration.
It seems you're trying to match a meal for each day of the week.
You can iterate on the days:
week_meals_options = {1: "Chicken", 2: "Soup"} # Your dict containing choices mapped to the meal options
week_meals = {}
for day in week_days:
while True:
answer = input(f"What would you like to eat on {day}? options are {week_meals_options.keys()}")
if answer in week_meals_options:
week_meals[d] = week_meals_options.pop(answer) # Remove the option from the dict, and assign it to the day
break
else:
print("That isn't an option!")

Python 3.6.2 loop not working like I wanted it too

I am currently writing a code for my GCSE coursework and I am kind of stuck with my for loop which also contains an if-else statement.
I have done a code similar to this earlier in the program and it works perfectly fine but for some reason this part doesn't and I was wondering if someone could help me.
What I am trying to do is make a quiz type program and the part that I need help with is choosing the subject that the user wants to do.
The user has to type in their preferred subject but if they type the subject in wrong, or type in something invalid, then the program should allow the user to type it in again.
So far, if you type in a subject correctly the first time, the program will proceed to the next stage.
However, if you type it incorrectly the first time, it will ask the user to try again. But if you type it in correctly the second time, it will again ask the user to try again. Instead of having the program make the user type the subject again, even though it should've been valid the when they typed it in correctly, I want the program to proceed to the next stage.
Available subjects:
subjects = []
algebra = ("algebra")
computing = ("computing")
subjects.append(algebra)
subjects.append(computing)
Part that I need help with:
with open("student_file.csv", "a+") as studentfile:
studentfileReader = csv.reader(studentfile, delimiter = ',')
studentfileWriter = csv.writer(studentfile, delimiter = ',')
print("Available subjects:\n-Algebra\n-Computing\n")
ChosenSubject = input("What subject would you like to do? ")
ChosenSubject.lower()
for i in range(2):
if ChosenSubject in subjects:
print("\n")
break
else:
print("\nPlease try again.")
ChosenSubject == input("What subject would you like to do?")
ChosenSubject.lower()
if ChosenSubject in subjects:
print("working")
else:
print("You keep typing in something incorrect.\nPlease restart the program.")
In the else block, perhaps you'd want to replace the '==' with '='.
Also do you want to give the user just two tries or keep asking them until they answer correctly? (The latter is what I inferred from your question, for that I'd recommend using continue)
The for loop just iterates over a collection of objects. Consider a list my_list = ['a', 'b', 'c']. On each iteration over my_list using for loop, it fetches one of the elements in order without repetition. range(2) is equivalent to [0, 1].
Try this:
print("Available subjects:\n-Algebra\n-Computing\n")
for i in range(2):
# `i` is 0 on first iteration and 1 on second. We are not using `i` anywhere since all we want is to loop :)
chosen_subject = input("What subject would you like to do? ")
if chosen_subject.lower() in subjects:
print("\n")
break
if chosen_subject.lower() in subjects:
print("working")
else:
print("You keep typing in something incorrect.\nPlease restart the program.")
This is not an optimal solution, but since your learning I will try to keep it as close as your solution. Your problem is that calling ChosenSubject.lower() does not change the actual value in ChosenSubject.
Here is a working example:
print("Available subjects:\n-Algebra\n-Computing\n")
ChosenSubject = input("What subject would you like to do? ")
subjects = ["algebra", "computing"]
for i in range(2):
if ChosenSubject.lower() in subjects:
print("\n")
break
else:
print("\nPlease try again.")
ChosenSubject = input("What subject would you like to do?") #not '=='
if ChosenSubject.lower() in subjects:
print("working")
else:
print("You keep typing in something incorrect.\nPlease restart the program.")
This from the doc:
This method returns a copy of the string in which all case-based
characters have been lowercased.

Issues with lists? Error checking not working

I am relatively new to python, and I just started learning how to use classes. This is the first program I've made where I've tried to integrate them, but I'm coming up with a small issue I can't seem to fix, and I think it has to do with lists. The code is as follows:
(The topic is getting the user to choose what type of seat to purchase).
class SeatBooking:
def __init__(self, seat):
self.seat = seat
possible_types = []
possible_types.extend(["Low_Economy", "Standard_Economy", "High_Economy",
"Business", "First", "Residence"])
possible_types = " ".join(possible_types)
while True:
if self.seat not in possible_types:
print("Sorry, but this is not a valid answer. Please try again!")
self.seat = str(input("What type of ticket would you like? The possible types are: {} "
.format(possible_types)))
else:
print("You have chosen to book a {} ticket.".format(self.seat))
confirmation = str(input("Please confirm with 'Yes' or 'No': ")).lower()
if confirmation == "yes":
print("Excellent decision! Ready to continue")
print("=" * 170)
break
elif confirmation == "no":
self.seat = str(input("What type of ticket would you like? The possible types are: {} "
.format(possible_types)))
else:
print("That doesn't seem to be a valid answer.")
Here is the main file (to execute the different classes I'll make):
import type_seat
# Choose the seat to book
print("=" * 170)
print("Welcome to Etihad! This program can help you organize your flight, payments and usage of miles!")
possible_types = []
possible_types.extend(["Low_Economy", "Standard_Economy", "High_Economy",
"Business", "First", "Residence"])
possible_types = " ".join(possible_types)
seat_type = str(input("What type of ticket would you like? The possible types are: {}. "
.format(possible_types)))
type_seat.SeatBooking(seat_type)
The problem I have is that I seem to be able to enter certain letters and it doesn't count them as an error even though they're not one of the available seats. For example, when I enter the letters "h" or "s", my error checking part of the code doesn't respond to it, but when I enter the letter "b" or random words like "try" it does. It doesn't seem to be completely random though, and it seems to only happen with letters or parts of the first 3 'items' in the possible_types[] list. However, I haven't tested this fully. This is why I thought it had something to do with lists, so if anyone knows what's causing this, I'd really appreciate it if they could help me resolve this and perhaps help me from repeating this mistake in the future!
Note, for the lists I am using .join, but I also tried str().
You don't have a list, you are testing characters against one long string:
possible_types = " ".join(possible_types)
The letters h and s are in that string (in the words High_Economy and Business, respectively), but the sequence try doesn't appear anywhere in the string.
If you only wanted to allow whole words to match, you'd need to leave possbile_types a list, or ideally convert it to a set (as sets allow for fast membership testing). You can define the list, no need for list.extend() here:
possible_types = ["Low_Economy", "Standard_Economy", "High_Economy",
"Business", "First", "Residence"]
or make it a set by using {...}:
possible_types = {"Low_Economy", "Standard_Economy", "High_Economy",
"Business", "First", "Residence"}
Do not join this into a string, just test directly against the object:
if self.seat not in possible_types:
If you still need to show the values to a user in an error message, join the values then, or store the str.join() result in a different variable for that purpose.
Note that you shouldn't deal with user input validation in the class __init__ method. Leave user interaction to a separate piece of code, and create instances of your class after you validated. That way you can easily swap out user interfaces without having to adjust all your data objects too.
possible_types = " ".join(possible_types)
Above statement will create one string as "Low_Economy Standard_Economy High_Economy Business First Residence".
Now you are doing
if self.seat not in possible_types:
This will check for a particular character in the string present or not. In your case you are finding 'h' which is present and 'try' which isn't.
Your program will work if you remove this statement
possible_types = " ".join(possible_types)

Python; List index out of range [address book task]

I'm currently studying Computer Science at GCSE level, and am nearing my first controlled assessment. Last week we moved onto learning about lists, but for the purpose of this weeks assignment, we need to create an improved version of our address book task using a 2D array. The purpose of the task is to allow the user to enter as many people as they require into an address book, and ask them for four pieces of information. This information is to be then printed onto the screen underneath.
Be that as it may, I'm encountering an error when entering more than one person into the address book, with the error list index out of range. I've read some posts on here already about the aforementioned error, but still don't seem to quite understand it fully. Any aid would be highly appreciated.
Here is my code thus far:
addressbook = ([])
number = int(input("How many people would you like in your address book:"))
for loop in range (0,number,1):
addressbook.append([(str(input("\nPlease enter your full name:")))])
addressbook.append([(str(input("Please enter your home address:")))])
addressbook.append([(str(input("Please enter your hometown:")))])
addressbook.append([(str(input("Please enter your mobile number:")))])
print("\nName:",addressbook[0][loop],"\nHomeaddress:",addressbook[1][loop],"\nHometown:",addressbook[2][loop],"\nMobile number:",addressbook[3][loop])
With the following error appearing:
Traceback (most recent call last):
File "C:\Users\Owner\Documents\Computer Science\Python\Address%20book.py", line 23, in <module>
print("\nName:",addressbook[0][loop],"\nHomeaddress:",addressbook[1][loop],"\nHometown:",addressbook[2][loop],"\nMobile number:",addressbook[3][loop])
IndexError: list index out of range
Rather than think of addressbook as a two-dimensional array of information about people, think of each dimension separately. An addressbook is a one-dimensional array of persons. Each person is, in turn, a one-dimensional array of information.
Bringing that thinking to our code can make the purpose much clearer:
# An address boook is an arary of persons
addressbook = []
number = int(input("How many people would you like in your address book:"))
# Create several persons, adding each to addressbook as we go:
for _ in range(number):
# Create one person:
person = []
person.append(str(input("\nPlease enter your full name:")))
person.append(str(input("Please enter your home address:")))
person.append(str(input("Please enter your hometown:")))
person.append(str(input("Please enter your mobile number:")))
# Add one person to addressbook
addressbook.append(person)
# Now addressbook has several persons
# Display addressbook, thinking of it as two-dim array
for loop in range(number):
print("\nName:",addressbook[loop][0],"\nHomeaddress:",addressbook[loop][1],"\nHometown:",addressbook[loop][2],"\nMobile number:",addressbook[loop][3])
# Display addressbook, thinking of it as array of persons:
for person in addressbook:
print("\nName:",person[0],"\nHomeaddress:",person[1],"\nHometown:",person[2],"\nMobile number:",person[3])
Other notes:
The statement addressbook = ([]) is identical to addressbook = []. Parentheses in that context do absolutely nothing.
Multi-dimensional arrays list the row number first, the column second. That is to say, the first number gets you to a row, and the 2nd number gets you to an element of that row. More simply, say addressbook[loop][0], not addressbook[0][loop].
The expression range(0, number,1) is identical to range(number), and the latter is easier to read.
In Python, if you need to assign a value but don't care about it further, use _, like so: for _ in range(number):.
The expression str(input(...)) is identical to the expression input(...), since input returns an object of type str. I left those alone because I suppose that verbose style is required by your instructor.
When the time comes, learn about breaking long expressions into multiple lines. Many people find that a line longer than about 80 characters is difficult to read.
You prompt the user "Please enter your full name" (emphasis added) each time. Presumably their name hasn't changed since the previous iteration of the loop. You are actually asking for some third party's full name. Try "Please enter their full name" or "Please enter this person's full name" or some such.
Finally, realize that computer programs will be read more often than they are written. This is true for professionally-maintained programs with hundreds of contributing programmers and also of one-off programs written by you for your own benefit. Use any means necessary to communicate effectively with the reader(s) of your program--comments, clear variable names, whitespace, whatever it takes.
First, Rob's explanation above is perfect for your assignment.
Now, I wanted to show you a few techniques, you can use, to make your life easier, without entering in OOP concepts and keeping the code as simple as possible, without error catching or whatsoever.
from collections import namedtuple
Person = namedtuple(
'Person',
('full_name', 'home_address', 'home_town', 'mobile_number')
)
person_print_template = '''
Full Name: {full_name}
Home Address: {home_address}
Home Town: {home_town}
Mobile Number: {mobile_number}'''
persons = []
number = int(input('How many people would you like in your address book: '))
for iteration in range(number):
full_name = input('\nFull name: ')
home_address = input('Home address: ')
home_town = input('Home town: ')
mobile_number = input('Mobile number: ')
person = Person(full_name, home_address, home_town, mobile_number)
persons.append(person)
for person in persons:
print(person_print_template.format(**person._asdict()))
collections.namedtuple is a way of defining really simple reusable objects and offers an _asdict() method which return a dictionary.
In python, putting ** in front of a dictionary allows you to unpack each of its dictionary keys and values which can be then passed, for example here, to a template, and str.format() will replace the "{key_name}" by its value.
Again this is a powerful way of formatting string output, especially when multi-line.=, either for the web or for command line output, like here.
Rob's answer, as Apero stated, is perfect. However as Apero addressed your format with functional programming, I'll address it in OOP (mostly because I love abstracting functional code! :D)
from collections import OrderedDict
# this is only necessary if you need your fields to always be in the
# same order when they're printed. Otherwise ignore and have Person
# inherit from dict
FIELDS = ["full name", "address", "hometown", "mobile number"]
class Person(OrderedDict): # see note above
def __init__(self, keys=None):
super().__init__() # this is magic that makes the OrderedDict work
if isinstance(keys, dict):
self.update(keys)
# this allows you to pass in a normal dict like you can for any
# other dict or ordered dict, and only prompt otherwise
else:
for key in keys:
self[key] = input("Please enter your %s: " % key)
def __str__(self):
"""The __str__ function describes how str(self) looks. I'm defining
it here to mimic the way Apero used his person_print_template string"""
output_strings = []
for key,value in self.items():
output_strings.append("{}: {}".format(key, value))
return "\n".join(output_strings)
# this is equivalently:
## return "\n".join(["{}: {}".format(key,value) for key,value in self.items()])
class AddressBook(list):
def __init__(self, size=None):
if size is None:
size = int(input("How many entries are in this address book? "))
for entry in range(size):
global FIELDS
self.append(Person(FIELDS))
def __str__(self):
return "\n\n".join([entry for entry in self])
# double spaced for readability
if __name__ == "__main__":
book = AddressBook()
print()
print(book)
# OUTPUT:
How many entries are in this address book? 2
Please enter your full name: Adam Smith
Please enter your address: 123 Some St.
Please enter your hometown: Eugene, OR
Please enter your mobile number: 555-867-5309
Please enter your full name: John Smith
Please enter your address: 987 Hello World, Ave.
Please enter your hometown: Camelot (tis a silly place)
Please enter your mobile number: 555-789-1234
full name: Adam Smith
address: 123 Some St.
hometown: Eugene, OR
mobile number: 555-867-5309
full name: John Smith
address: 987 Hello World, Ave.
hometown: Camelot (tis a silly place)
mobile number: 555-789-1234
You can certainly make changes in format to suit. I'd point you at the str.format method so you can do things like justify your strings beautifully
# # inside Person.__str__
for key,value in self.items():
global SCREENWIDTH # perhaps 50?
output_strings.append("{0}: {1:>{2}}".format(key,value, SCREENWIDTH - len(str(key)) - 1))
full name: Adam
address: blah
hometown: doohickey
mobile number: 1234
Possibly a flag on AddressBook.init that works as an alternate constructor given an existing list of Person objects.
class AddressBook(list):
def __init__(self, argument=None, _prompted=True):
if _prompted:
size = argument # just for clarity
self.from_prompt(size)
else:
lst = argument # just for clarity
self.from_list(lst)
def from_prompt(self, size):
# exactly as __init__ is above
def from_list(self, lst):
self.extend(lst)
Lots of fun stuff to be done with OOP. Personally I like them just because of Python's duck typing only becomes stronger when I can control both what kind of quack to listen to and what kind of quack to MAKE! :)
Use this instead
for loop in range (0,number,1):
addressbook.append([])
addressbook[-1].append([(str(input("\nPlease enter your full name:")))])
addressbook[-1].append([(str(input("Please enter your home address:")))])
addressbook[-1].append([(str(input("Please enter your hometown:")))])
addressbook[-1].append([(str(input("Please enter your mobile number:")))])
print("\nName:",addressbook[-1][0],"\nHomeaddress:",addressbook[-1][1],"\nHometown:",addressbook[-1][2],"\nMobile number:",addressbook[-1][3])
The problem with your code was you were using single dimension list. For every loop you are adding the items into addressbook so after 2 iterations of the loop it will look smthing like this
["name1","addr1","hmtown1","mob1","name2","addr2","hmtown2","mob2"]
So instead solution would be add an empty list for every iteration in loop and to that empty list we add the details so the structure would look like this
[["name1","addr1","hmtown1","mob1"],["name2","addr2","hmtown2","mob2"]]
In every iteration we access the last list by addressbook[-1] and then append the items to that last list, similarly accessing the items by using addressbook[-1][0].
Hope this helped you understand the problem.

Categories