Editable Callers for Class Attributes - Python - python

I am working on a mock Student Database using OOP in python, and I am trying to use a function to search for certain parameters of a Class.
In the following example, School is a large class that holds instances of Students as one of its arguments. Hence (For Student in School.Student)
found_list = []
class Student():
def __init__(self, name, age, gender, name_of_school, class_type):
self.name = name
self.age = age
self.gender = gender
self.name_of_school = name_of_school
self.class_type = "S"
Example_Student = Student("Joseph", 8, "male", "The School", "S")
gender_to_be_found = input("Enter the gender to be searched for ")
for Student in School.Student:
if Student.gender == gender_to_be_found:
found_list.append(Student)
This works as a principle but I am wanting to do it in the below format, so I can search for various attributes of a Class through one function
def search_for(value_to_be_found, values_to_searched_from, end_list, class_argument):
if end_list != values_to_searched_from:
for possible_targets in value_to_be_found:
for possible_matches in values_to_searched_from:
try:
if possible_targets == possible_matches.class_argument:
end_list.append(possible_matches)
except:
pass
else:
for possible_targets in value_to_be_found:
for possible_matches in values_to_searched_from:
try:
if possible_targets != possible_matches.class_argument:
end_list.remove(possible_matches)
except:
pass
so that I can pass in the (class_argument) "gender"
and automatically search for any Student.gender that matches my value_to_be_found
search_for("Joseph", School.Student, found_list, "name")
Clearly this proposed method (above) is non-functional, but I feel like there is a way to do this that I have not managed to quite achieve.
This error is produced:
AttributeError: object has no attribute 'class_argument'
Thanks in advance for any help :)

You could add a search function to School, using getattr to access attributes of an object:
class Student():
def __init__(self, name, age, gender, name_of_school, class_type):
self.name = name
self.age = age
self.gender = gender
self.name_of_school = name_of_school
self.class_type = "S"
class School:
def __init__(self):
self.students = []
def add(self, student):
self.students.append(student)
def search(self, value, search_attribute):
result = []
for s in self.students:
student_value = getattr(s, search_attribute, None)
if student_value == value:
result.append(s)
return result
s1 = Student("Joseph", 8, "male", "The School", "S")
s2 = Student("Joseph2", 9, "male", "The School", "S")
s3 = Student("Joseph3", 10, "male", "The School", "S")
s = School()
s.add(s1)
s.add(s2)
s.add(s3)
print(s.search("Joseph", "name"))
print(s.search(10, "age"))
print(s.search("gender", "binary"))
print(s.search("The School", "name_of_school"))
Out:
[<__main__.Student object at 0x107c19fd0>]
[<__main__.Student object at 0x107c19ee0>]
[]
[<__main__.Student object at 0x10ab16fd0>, <__main__.Student object at 0x10ab16fa0>, <__main__.Student object at 0x10ab16ee0>]

Related

How to call object of class by input?

I am generating a class of persons and want to get information about a certain person by input. I would like to use the str funtction because I am trying to understand it better. My Idea goes as follows:
class Person:
__init__(self, f_name, l_name):
self.f_name = f_name
self.l_name = l_name
__str__(self):
return "The persons full name is:" + f_name + l_name
person1 = Person(Peter, Punk)
person2 = Person(Mia, Munch)
person = input("What persons full name would you like to know?")
print(person) #I am aware that this just fills in the string saved in person, but how do I connect it to the variable?
another idea was to do it as follows:
#class stays the same except:
__init__(self, f_name, l_name):
self.f_name = f_name
self.l_name = l_name
list.append(self)
#and then for the main:
list = []
person1 = Person(Peter, Punk)
person2 = Person(Mia, Munch)
person = input("What persons full name would you like to know?")
index = list(person)
print(list[index])
Thankful for any edvice since I am obviously new to Python :D
I think OP has some concept problems here which this answer may go some way to help with.
Start by building a robust class definition. Simple in this case as there are just 2 attributes. Note the use of setters, getters and str, repr and eq dunder overrides.
A small function that checks if a given Person can be found in a list of Persons and reports accordingly.
Create a list with 2 different Person instances
Create another Person that is known not to match anything already in the list.
Run check()
Modify the 'standalone' Person to make it equivalent to something previously constructed.
Run check()
class Person:
def __init__(self, forename, surname):
self._forename = forename
self._surname = surname
#property
def forename(self):
return self._forename
#forename.setter
def forename(self, forename):
self._forename = forename
#property
def surname(self):
return self._surname
#surname.setter
def surname(self, surname):
self._surname = surname
def __str__(self):
return f'{self.forename} {self.surname}'
def __repr__(self):
return f'{self.forename=} {self.surname=}'
def __eq__(self, other):
if isinstance(other, type(self)):
return self.forename == other.forename and self.surname == other.surname
return False
def check(list_, p):
if p in list_:
print(f'Found {p}')
else:
print(f'Could not find {p}')
plist = [Person('Pete', 'Piper'), Person('Joe', 'Jones')]
person = Person('Pete', 'Jones')
check(plist, person)
person.surname = 'Piper'
check(plist, person)
Output:
Could not find Pete Jones
Found Pete Piper
You probably want a mapping between a name and an object. This is what Python's dict dictionary structure is for:
people = {} # an empty dictionary
people[f'{person1.f_name} {person1.l_name}'] = person1
people[f'{person2.f_name} {person2.l_name}'] = person2
This is creating a string of the first and last name.
You can then lookup the Person object using the full name:
print(people['Peter Punk'])
You could do this with list comprehension like so (also allowing multiple people to have the same first name)
class Person:
__init__(self, f_name, l_name):
self.f_name = f_name
self.l_name = l_name
__str__(self):
return "The persons full name is:" + f_name + l_name
personList= []
personList.append(Person(Peter, Punk))
personList.append(Person(Mia, Munch))
personName = input("What persons full name would you like to know?")
print([str(person) for person in personList if person.f_name == personName])

How to add data on a dictionary using different multiple user input inside a class?

I have a simple program that takes user name and age using user input. How can I store the data on dictionary and update the data if another user put new name and age. Here is my sample code. I don't know if I'm doing it right.
class Name:
data = {}
num_employee = 0
def __init__(self, name, age):
self.name = name
self.age = age
Name.num_employee += 1
#classmethod
def user_in(cls):
name = input('Enter name: ')
age = int(input('Enter age: '))
return cls(name, age)
def show(self):
Name.data = {'name': self.name, 'age': self.age}
return Name.data
employ = Name.user_in()
employ2 = Name.user_in()
print(Name.num_employee)
print(employ.show())
Every instance of a Name class would be a person with name and age. Now i'm not getting if you're supposing an employee can have more than one name (and this is why you need a dictionary) or you simply need an object to collect information about every user.
If you want to mantain the input inside of the class move it to the constructor, that is __init__ method.
I would use another object such as a list to collect the set of users.
I also added two method to the class Person allowing user to modify age and name with a new input.
class Person:
def __init__(self):
self.name = input('Enter name: ')
self.age = int(input('Enter age: '))
def show(self):
data = {'name': self.name, 'age': self.age}
return data
def change_name(self):
self.name = input('Update name: ')
def change_age(self):
self.age = int(input('Update age: '))
persons = []
employ = Person()
employ2 = Person()
# add employ to the list
persons.append(employ)
persons.append(employ2)
# to show information
print(len(persons)) # len of the list is the number of employees
print(employ.show())
# to change employ1 name you can do
employ.change_name()
# to change employ2 age do
employ2.change_age()

Python object creating a group with 3 members of aggregation relationship

I had an assignment to create a python code using class to create a group with 3 members (aggregation relationship). This is my code so far:
class Member:
def __init__(self,name,age):
self.name = name
self.age = age
def getInfo(self):
memberInfo = "Name: " + str(self.name) + "." + "Age: " + str(self.age)
return memberInfo
class Group:
def __init__(self,name):
self.name = name
self.memlist = []
def addMember(self,member):
self.memlist.append(member)
def getInfo(self):
info = "Member List: \n"
for i in range(len(self.memlist)):
info += self.memlist[i].getInfo() + "\n"
print(info)
break
mem1 = Member("Chi",20)
mem2 = Member("Bach",7)
mem3 = Member("Gen", 22)
group1 = Group("Siblings")
group1.addMember(mem1)
group1.addMember(mem2)
print(group1.getInfo())
print(mem2.getInfo())
print(group1.memList)
But it has shown an error: AttributeError: 'Group' object has no attribute 'memList'. Is there anything I can do to fix this?
I wrote little function for listing members and their ages.
class member:
def __init__(self, name, age):
self.name = name
self.age = age
def member_Info(self):
memberInfo = f"Name: {str(self.name)}-->Age: {str(self.age)}"
return memberInfo
class Group:
def __init__(self, name):
self.name = name
self.memlist = []
def addMember(self, name):
self.memlist.append(name)
def getInfo(self):
for i in range(len(self.memlist)):
info = self.memlist[i].member_Info() + "\n"
print(info)
This all_members function is basically getting the information stored in the member class and return to list. I print using memlist in Group but it didn't work out so I made a new list using all_member function and get information from memlist in group1 with the code that you used for getting information in memlist at group1.getInfo .
def all_members():
all_mems = []
for i in range(len(group1.memlist)):
all_mems.append(group1.memlist[i].member_Info())
print(all_mems)
mem1 = member("Chi", "20")
mem2 = member("Bach", "7")
mem3 = member("Gen", "22")
group1 = Group("Siblings")
group1.addMember(mem1)
group1.addMember(mem2)
group1.addMember(mem3)
print(group1.getInfo())
print(mem2.member_Info() + "\n")
print(all_members())
I guess this isn't the best answer you can get but I think it will work and also I learn many things while trying to correct it so thank you for posting that.
change
print(group1.memList)
to
print(group1.memlist)

Understanding Inheritance

I'm trying to understand inheritance better. In the following code, when I try to print friend.salary, it throws an AttributeError. Doesn't WorkingStudent inherit all methods of the Student class?
class Student:
def __init__(self,name,school):
self.name = name
self.school = school
self.marks = []
def average(self):
return sum(self.marks)/len(self.marks)
def friend(self,friend_name):
return Student(friend_name, self.school)
anna = Student("Anna","MIT")
friend = anna.friend("Motilal")
#print (friend.name)
#print (friend.school)
class WorkingStudent(Student):
def __init__(self,name,school,salary):
super().__init__(self,name,school)
self.salary = salary
anna = WorkingStudent("Anna","SXS",25000)
anna.friend("Greg")
anna.marks.append(50)
print friend.salary
You should modify your source code as below
class Student:
def __init__(self,name,school):
self.name = name
self.school = school
self.marks = []
def average(self):
return sum(self.marks)/len(self.marks)
def friend(self,friend_name):
return Student(friend_name, self.school)
anna = Student("Anna","MIT")
friend = anna.friend("Motilal")
#print (friend.name)
#print (friend.school)
class WorkingStudent(Student):
def __init__(self,name,school,salary):
super(WorkingStudent,self).__init__(name,school)
self.salary = salary
# anna = WorkingStudent("Anna","SXS",25000)
def friend(self,friend_name):
return WorkingStudent(friend_name, self.school, self.salary)
# You should put your code here, because as your original code
# anna is an instance of Student not WorkingStudent class
# so it and its friend don't have "salary".
anna = WorkingStudent("Anna","SXS",25000) # After this line, anna is a different variable to "anna" variable that was defined before (anna = Student("Anna","MIT"))
friend = anna.friend("Greg") # friend now is an instance of WorkingStudent class, so it have salary
anna.marks.append(50)
print(friend.salary)
Editted. So code can work now

use/execute a string within a list as an object name (python)

It looks like this:
I define a class:
class Boy():
def __init__(self):
self.age = input()
self.height = input()
Then I define a list with the names of the boys that I want to be object instances of the above 'class Boy':
boys = [input(), input()]
(for example: john & frank, so that boys = ['john', 'frank'])
I want now to iterate over my list 'boys' and use each name to make an object of the 'class Boy':
for value in boys:
value = Boy()
Of course, it does not work :-) but is there a way to achieve it ??
I have been using Python since 1 week, if the question sounds silly :-)
If someone could help me, will be very thankful
Thank you all for the help, I implemented the proposed solutions:
thank_you_people = ['Makoto','L3viathan','Rcynic','Pythonic','Paul Rooney', 'st.eve']
:-)
for person in thank_you_people:
print('Thank you, %s' % person)
I would highly recommend changing your class up a bit, to remove input() calls from the constructor. You could use an __init__ method that has optional arguments for age and height, and a forced one for name:
class Boy():
def __init__(self, name, age=None, height=None):
self.age = age
self.height = height
self.name = name
You then can instantiate with a name, and assign the attributes later:
boys = [Boy(input("New boy: ")), Boy(input("New boy: "))] # e.g. "John", "Frank"
for boy in boys:
boy.age = input("Age of",boy.name + "?")
boy.height = input("Height of",boy.name + "?")
edit: To have the boys in a dictionary for easier access:
boys = {}
for _ in range(2):
name = input("New boy:")
boys[name] = Boy(name)
for boy in boys:
boys[boy].age = input("Age of",boys[boy].name + "?")
boys[boy].height = input("Height of",boys[boy].name + "?")
Don't use the input function in your __init__ method, as it restricts how your Boy class can be used. Instead define a separate function that creates a Boy and pass those parameters to your Boy constructor. Then you could store your Boys in a dict, keyed by the boys name.
e.g.
class Boy():
def __init__(self, age, height):
self.age = age
self.height = height
def __str__(self):
return 'age=%d height=%d' % (self.age, self.height)
def create_boy(name):
age = int(input('enter age for %s: ' % name))
height = int(input('enter height for %s: ' % name))
return Boy(age, height)
boynames = []
while(True):
name = input('enter boy name: ')
if name == '':
break
boynames.append(name)
boys = {}
for boyname in boynames:
boys[boyname] = create_boy(boyname)
for name, boyinfo in boys.items():
print('name=%s %s' % (name, boyinfo))
Later on you could query a particular boys name like so
if 'paul' in boys:
print(boys['paul'].age)
print(boys['paul'].height)
else:
print('no boy called paul')
This isn't designed to work if you can have Boys with the same name. In that case you could have each dictionary entry hold a list of Boys and find some other parameter to distinguish between the Boys of the same name.
To not use just anything as an object name, you can use the input as keys for a dict. Something like this:
class Boy():
def __init__(self):
self.age = input()
self.height = input()
boys = [input(), input()] # this is probably a bad idea
b = {}
for boy in boys:
b[boy] = Boy()
I'd suggest to i) use a dictionary for creating variables with the name contained in a string and ii) pass age and height as args, **kwargs or *args in the class, as already suggested by Makoto. Something along these lines:
class Boy():
def __init__(self, name, age=0, height=0):
self.age = age
self.height = height
boys = ['finn', 'jake']
ages = [16, 33]
height = [165, 120]
boys_objects = {}
for i, b in enumerate(boys):
boys_objects[b] = Boy(age=ages[i], height=heights[i])
Use the zip function. You'll have to change your class constructor a little bit though.
class Boy(object):
def __init__(self, (name, age, height)):
self.name = name
self.age = age
self.height = height
Then get the input values into lists
names = ['a','b','c']
ages = [10, 20, 30]
heights = [155, 160, 165]
boys = zip(names, ages, heights)
for guy in boys:
b = Boy(guy)
print b.name, b.age, b.height
I don't know wha you want to do with the objects - but you can change that in the for loop.
EDIT: In response to the error OP is getting in the comments:
I cannot reproduce the init error. I tried with python and iPython both.
Alternatively you could try
def __init__(self, guy_tuple):
self.name = guy_tuple[0]
self.age = guy_tuple[1]
self.height = guy_tuple[2]
If that doesn't work either, you can change the constructor to take in name, age and height individually. Like is working for you.
def __init__(self, name, age, height):
self.name = name
...
then the for loop must change accordingly. Since guy is a tuple with all three elements, you'll have to get each one by index within the tuple.
for guy in boys:
b = Boy(guy[0], guy[1], guy[2])
Yes object b will be overwritten, you'll have to change it to do what you need it to do once you get the object. So after b is instantiated, pass it to another function to do what you want with it, or append it to a list. Once b is an object, you continue with the logic you want.

Categories