How to compare user input to class object data? - python

total beginner here.
I'm trying to understand how to compare user input data to preset class data. Let's say class = Person, with the age, weight, and name.
And I've got two people, johnsmith = Person(50, 200, "John Smith")
tomjones = Person(40, 220, "Tom Jones")
I want to prompt the user to input the name Tom, and have check all "name" attributes for the Person class. So something like,
person = input(print("Insert the person's name: ")) entering "Tom" and then returning a list of all Toms for the user to select, or if there's only one, confirm that the user in fact did mean Tom Jones.
Should I create an array like people = [tomjones, johnsmith, (etc)], and somehow search inside that?
Please point me in the right direction on this, thanks.

Should I create an array-like people = [tomjones, johnsmith, (etc)], and somehow search inside that?
Yes.
Although ihough if you expect a very large number of "people" and frequent queries always on the same field (e.g. you're always searching for a specific person by its name) you may also want to create an index to speed up searches e.g. create a dict (possibly a WeakValueDictionary) which maps whatever your search key is to the proper person.
This is because iterating a list is cheap and efficient but going through the entire list is linear time (O(n)) so if your list is very long and you go through it a lot, it adds up. Building an index is expensive (and requires going through the entire list at least once) and there is more setup to the lookup but setup aside the lookup is constant time (O(1)).

Storing the instances of your Person class in a list would be a way of doing this. You can then loop through the list to match the names.
e.g.
class Person():
def __init__(self, age, weight, name):
self.age = age
self.weight = weight
self.name = name
all_persons = []
all_persons.append(Person(50, 200, "John Smith")) # add your Person instance to a list
all_persons.append(Person(40, 220, "Tom Jones"))
name_input = input("Insert the person's name: ")
for person in all_persons: # loop through list of all Person instances
if name_input.lower() in person.name.lower(): # lower() to handle case
# do something with person

Related

Case insensitive Full Name dictionary search

I am creating a dictionary with "Full Name": "Birthday" for numerous people as an exercise.
The program should ask
"Who's birthday do you want to look up?"
I will input a name, say "Benjamin Franklin"
And it will return his birthday: 1706/01/17.
Alright, the problem I am encountering is name capitalization.
How can I input "benjamin franklin" and still find "Benjamin Franklin" in my dictionary? I am familiar with .lower() and .upper() functions, however I am not able to implement them correctly, is that the right way to approach this problem?
Here is what I have
bday_dict = {"Person1": "YYYY/MM/DD1",
"Person2": "YYYY/MM/DD2",
"Benjamin Franklin": "1706/01/17"}
def get_name(dict_name):
name = input("Who's birthday do you want to look up? > ")
return name
def find_bday(name):
print(bday_dict[name])
find_bday(get_name(bday_dict))
The best way to do this is to keep the keys in your dictionary lowercase. If you can't do that for whatever reason, have a dictionary from lowercase to the real key, and then keep the original dictionary.
Otherwise, Kraigolas's solution works well, but it is O(N) whereas hashmaps are supposed to be constant-time, and thus for really large dictionaries the other answer will not scale.
So, when you are setting your keys, do bday_dict[name.lower()] = value and then you can query by bday_dict[input.lower()].
Alternatively:
bday_dict = {"John": 1}
name_dict = {"john": "John"}
def access(x):
return bday_dict[name_dict[x.lower()]]
Probably the most straight forward way I can think of to solve this is the following:
def get_birthday(name):
global bday_dict
for person, bday in bday_dict.items():
if name.lower() == person.lower():
return bday
return "This person is not in bday_dict"
Here, you just iterate through the entire dictionary using the person's name paired with their birthday, and if we don't find them, just return a message saying we don't have their birthday.
If you know that all names will capitalize the first letter of each word, you can just use:
name = ' '.join([word.capitalize() for word in name.split()])
then you can just search for that. This is not always the case. For example, for "Leonardo da Vinci" this will not work, so the original answer is probably the most reliable way to do this.
One final way to do this would be to just store the names as lowercase from the beginning in your dictionary, but this might not be practical when you want to draw a name from the dictionary as well.
Depending what your exercise allows, I would put the names in the dictionary as all lowercase or uppercase. So:
bday_dict = {"person1": "YYYY/MM/DD1",
"person2": "YYYY/MM/DD2",
"benjamin franklin": "1706/01/17"}
And then look up the entered name in the dictionary like this:
def find_bday(name):
print(bday_dict[name.lower()])
You may also want to do a check that the name is in the dictionary beforehand to avoid an error:
def find_bday(name):
bday = bday_dict.get(name.lower(), None)
if bday:
print(bday)
else:
print("No result for {}.".format(name))

Why can't I get list to access a class, properly?

I'm new to Python. I'm making a basic list that has access to a class. But when I send it to the output, the terminal lists it as a basic object, instead of each class item seperately.
Here is the code:
# Make a class with attributes
class Persons(object):
def __init__(self, firstName=None, lastName=None):
self.firstName = firstName
self.lastName = lastName
# Make list with said attributes
def newList():
nameList = []
nameList.append(Persons("Mathew", "Dodson"))
nameList.append(Persons("Dr", "Kin"))
# Print out said list
print(str(nameList.firstName))
print(str(nameList.lastName))
newList()
I'm pretty sure I have the syntax entirely wrong some way. If anyone could help, that would be appreciated.
Make the list
To make list you don't need to do successive appends, just declare it in one go like so:
name_list = [Persons('Mathew', 'Dodson'), Persons('Dr', 'Kin')]
Iterate the list
The list itself has no first_name attribute. But the elements do.
for person in name_list:
print(person.first_name)
print(person.last_name)
I'm sorry to change your naming convention, but everyone uses snake case in python. You may explore further in PEP8.
nameList is a list - it contains Person objects. nameList.firstname will not work. You will need to select one of those Persons inside nameList, and then access the attributes. For example:
# Print out said list
print(str(nameList[0].firstName)) # Mathew
print(str(nameList[1].lastName)) # Kin
If you want to print out the whole list:
for person in nameList:
print(person.firstName)
print(person.lastName)
nameList is a list, lists don't have a firstName or lastName attribute (there is no magic by which lists automatically access attributes of their members).
Similarly, you say "the terminal lists it as a basic object, instead of each class item separately." That's because just like lists, object's have a repr that's fairly uninformative, but unlike lists, you can customize it.
If the goal is to print out each person's first name and last name, you could do something like:
for person in nameList:
print(person.firstName, person.lastName) # Separates with a space
You could also achieve this by defining a __str__ or __repr__ for your class such that a more useful string form is produced automatically, e.g.:
class Persons(object):
def __init__(self, firstName=None, lastName=None):
self.firstName = firstName
self.lastName = lastName
def __repr__(self):
return 'Persons({!r}, {!r})'.format(self.firstName, self.lastName)
def __str__(self):
return '{} {}'.format(self.firstName, self.lastName)
which would allow you to get the same results as the first loop I described with just:
for person in nameList:
print(person) # __str__ is called automatically, and you made it return something useful

The best suggestion for a structure to store multiple keys and values - dictionaries?

The intention is to create the beginnings of a facebook type program (For teaching purposes) that stores a list of names of individuals along with their bio details.
I have two questions, one follows on from the next:
Question 1: How to get at the list values that are part of a value in a key value pair in a dictionary. For instance, to find out what friends John and Mary have in common, in this case friend1 and friend3
Question 2: The best method to create a structure that stores names,gender,hobbies,and friends? Would this be a dictionary and if so, how could this be defined? If not, what do people suggest?
#create a dictionary that stores names, and a list of friends
facebook_profile={"John":["friend1","friend2","friend3","friend4"],"Mary":["friend1","friend7","friend3","friend9"]}
print(facebook_profile)
Required to store and subsequently print the following sample data:
Name:John
Gender: Male
Hobbies: Chess
Friends: friend1,friend2,friend3,friend4
Name: Mary
Gender: Female
Hobbies: Chequers
Friends: friend1,friend2,friend3,friend4
I am aware the best solution would be a database and to use some sort of file handling to achieve it however, for teaching purposes we are trying to use only lists or dictionaries. These dictionaries/lists could then be written to a file, but the solution(s)/answers I am looking for must ideally be utilising only the list and dictionary structures.
Another way is to store in Database with table columns and firends having many-to-many relationship with that table.
For Question 1, a set is a good choice for quickly and easily calculating intersections.
For Question 2, a dictionary works well.
For example:
facebook_profile={
"John":{"friends":{"friend1","friend2","friend3","friend4"},"Gender": "Male"},
"Mary":{"friends":{"friend1","friend7","friend3","friend9"},"Gender": "Female"}
}
mutual_friends = facebook_profile["John"]["friends"].intersection(facebook_profile["Mary"]["friends"])
print (mutual_friends)
Gives output:
{'friend1', 'friend3'}
Create a class:
class Person:
def __init__(self, name, gender, hobbies, friends):
self.name = name
self.gender = gender
self.hobbies = hobbies
self.friends = friends
def getMutualFriends(self, personB):
return list(set(personB.friends).intersection(self.friends))
person1 = Person('John', 'male', ['Chess'], ['friend1', 'friend2'])
person2 = Person('Anna', 'female', ['Soccer'], ['friend1', 'friend3'])
print(person1.getMutualFriends(person2))
EDIT You edited your answer to state you want lists and dicts only, So maybe this is not for you, however, classes are the best way to achieve complicated storage of information about similar repetitive objects with many features, so i'll just leave this here
As this gets more and more complicated, nested dictionaries can be a real headache, I would suggest you define a class, something like this:
class Person()
def __init__(self, name, gender):
self.name = name
self.gender = gender
For that class you can define all sorts of methods, like finding friends in common:
class Person()
def __init__(self, name, gender):
self.name = name
self.gender = gender
self.friends = []
self.hobbies = []
def add_friends(self, list_of_friends):
self.friends += list_of_friends
def add_hobbies(self, list_of_hobbies):
self.hobbies += list_of_hobbies
def mutual_friends(self, another_person):
return list(set(another_person.friends).intersection(self.friends))
Then all you have to do is initialize each friend and start running all sorts of methods
john = Person('John', 'Male')
john.add_friends(['friend1', 'friend2', ...]
mary = Person('Mary', 'Female')
mary.add_friends(['friend3', 'friend7', ...]
common_friends = john.mutual_friends(mary)
print(common_friends) # Will print a list of mutual friends
And like others said, for long lists of friends a more efficient way is to use set with intersection

Changing attributes based on user input

Okay, I have a class which has 10 objects, these have the attributes self.planet, self.distance, self.distsquared, self.radius, self.diamater where distance/distsquared/radius/diamater are all integers. And I would like to make a function where the user searches for a planet name, and then changes one of the attributes.
For example, the user should input the name "Jupiter", which would then find the object, and the next row of the function would ask the user to add a certain sum to the attribute self.distance.
Currently the first class is set up as following:
class Planets():
def __init__(self, planetName, dist, radius, diameter):
self.planetName= planetName
self.dist= dist
self.radius= radius
self.diameter= diameter
This is then retrieved through a planetObjects=[Planets(*p) for p in planetList] This is the object-list I would like to turn into a dictionary so the user can search for planetName, and alter the distance
Some users suggested I use a dictionary for this, but I have no idea how to go about doing that. Currently my class turns a list of lists into a list of objects, these objects have these attributes that the user is supposed to be able to change by searching for the Planet name, and then changing one of its attributes.
The class is currently just a simple class which has a constructor and a __str__ function
Meaning, function starts, asks the user something like "Which planet would you like to alter?", the user inputs "Jupiter" and the program asks, "How has the distance to Jupiter changed?" where the user adds for example 450 or so.
The current code I have is a function which opens an infile and turns it into a list of lists. This list is then turned into objects. I turned it into objects to easily be able to sort it and add new values based on previous values. But at this point the user also has to be able to alter values by searching for a planet name and then changing one of the attributes - this is where I am lost and need help!
Is there any way to do this? Thanks in advance!
In psuedocode:
class Planet(object):
# Define your planet class here
# any attributes that you do NOT need the user to be able to edit should start with _
Planets = [Planet('Mercury'.....
#or better
PlanetDict = {'Mercury':Planet(....
which = PromptUserForPlanet()
p = PlanetDict.get(which) # or find and return it if you didn't use a dictionary
for att in dir(p):
if not att.startswith('_'):
input = raw_input('%s: (%s)' % (attr, repr(getattr(p,attr)))
if len(input) > 0:
setattr(p,att,input) # You may wish to do some type conversion first
Since p is a reference to the dictionary entry you will change the main object.
Given your class Planets, this may be solved like this. I'm assuming that planetList is structured like in this code. If it is not, you may have to alter the code a bit.
def increment_dist(planets):
name = raw_input('Please enter planet name')
try:
planets[name].dist += int(raw_input('Increment distance by (integer)'))
except KeyError:
print('No planet called {}'.format(name))
except ValueError:
print('That is not an integer')
planetList = [('Tellus', 1, 2, 4), ('Mars', 1, 3, 9)]
planet_dict = {name: Planets(name, dist, radius, diameter) for
name, dist, radius, diameter in planetList}
increment_dist(planet_dict)

Python 3 Classes and Dictionary

I'm working on a project here and am pretty confused as of how to handle this next part. Included is the project description.
Student class: The student class will store the information about the student id, student’s first and last names, and a dictionary named grades that contains the course number and letter grades of the classes the student has taken. You will write accessor methods for student id, first name, and last name and mutator methods for student’s first name and last name. There will be two more methods in the Student class as shown below.
getCourseNumbers(): it returns a list of course numbers that the student has taken. It returns an empty list if the student has not taken any courses.
getGrade(course_no): it returns the grade the student has received in the course identified by the parameter course_no. If the given course number is not found, this function returns ‘Z’ to indicate that.
addCourse(course_no, grade): adds a course with the course number and grade
updateCourse(course_no, grade): updates an existing course’s grade
deleteCourse(course_no): deletes the course from the dictionary.
Here is my code thus far:
class Course:
def __init__ (self, __crn, __ch, __ins):
self.__crn = crn
self.__ch = ch
self.__ins = ins
def coursename(self):
return self.__crn
def credithour(self):
return self.__ch
def instructor(self):
return self.__ins
class Student:
def __init__(self, id, sfirst, slast):
self.sid = sid
self.sfirst = sfirst
self.slast = slast
def studentid(self):
return self.sid
def studentfirst(self):
return self.sfirst
def studentlast(self):
return self.slast
def main():
course = Course('CSC 1100', '4', 'Name')
print (course.coursename())
print (course.credithour())
print (course.instructor())
main()
So, my question really is. Now that I've made my initial two classes, I'd like to create a dictionary that contains the coursename and the students grade. What's the best way to do this? I'm kind of at a loss and have tried many different ways with no success.
Why do you have a Course class? Nothing in the question asks for one; in fact, it specifies that you are to store the information as a dictionary.
I don't agree with the request for accessor methods; that is a Java idiom, not Pythonic. Similarly, the given method names are javaCase, contrary to PEP8.
The code then reduces to
class Student(object):
def __init__(self, id, firstname, lastname, grades=None):
self.id = id
self.firstname = firstname
self.lastname = lastname
self.grades = {} if grades is None else dict(grades)
def get_course_numbers(self):
return self.grades.keys()
def get_grade(self, course):
return self.grades.get(course, 'Z')
def add_course(self, course, grade):
self.grades[course] = grade
... I'll leave the last couple of methods as an exercise ;)
Recall that a dictionary is just a key-value mapping - it associates keys with particular values. So what you're being asked for is just storing with each student a mapping from a unique identifier for a course to a letter grade (stored e.g. as a string). A dictionary is an excellent tool for this task.
Recall that you can use class instances as keys, even if instances of that class are mutable. The reason is that it's not really the instance itself being used as a key, but its unique identifier (memory address).
It would probably be more apt, though, to have a way to map course numbers to Course instances. Notice the keyword map there? You could also store all the courses in a list and do a search every time you wanted to find a course by number, but that would be slow (for len(courses) -> Infinity).

Categories