So I am still a beginner programmer who has been tasked with sorting objects created by a csv file with the attributes lname, fname, gender, age (in that order) and sort them by the lname attribute. I have achieved this, however I now need to delete one of the objects (I chose a random one to test) and this is what I have so far:
class FitClinic:
def __init__(self, lname, fname, gender, age):
self.lname = lname
self.fname = fname
self.gender = gender
self.age = int(age)
def __del__(self):
print("Customer has been deleted")
def get_lname(self):
return self.lname
def get_fname(self):
return self.fname
def get_gender(self):
return self.gender
def get_age(self):
return self.age
fh=open('fit_clinic_20.csv', 'r')
fh.seek(3)
listofcustomers=[]
for row in fh:
c = row.split(",")
listofcustomers.append(FitClinic(c[0], c[1], c[2], c[3]))
sorted_list=sorted(listofcustomers,key=lambda x: x.get_lname())
for x in sorted_list:
if x.get_lname()==("Appleton"):
del x
print(x.get_lname(),x.get_fname(),x.get_gender(),x.get_age())
now it obviously doesnt work and I need some help.
del x just deletes the temporary variable x, it has no effect on the list. You need to use del listofcustomers[pos], but first you have to find the position in the list.
try:
pos = next(i for i,v in enumerate(listofcustomers) if v.get_lname() == "Appleton")
del listofcustomers[pos]
except StopIteration:
pass // Ignore if not found
See Python: return the index of the first element of a list which makes a passed function true for numerous ways to find the index of an element that matches a criteria.
You can remove an item from a list with list comprehension:
sorted_list[:] = [x for x in sorted_list if not(x.get_lname()==("Appleton"))]
A working example:
class FitClinic:
def __init__(self, lname):
self.lname = lname
def __del__(self):
print("Customer has been deleted")
def get_lname(self):
return self.lname
# Create example
sorted_list = [FitClinic('a'), FitClinic('b'), FitClinic('c'), FitClinic('Appleton')]
sorted_list[:] = [x for x in sorted_list if not(x.get_lname()=="Appleton")]
Now sorted_list is.
a
b
c
This example is better with filter since it removes all clinics where lname is Appleton:
sorted_list = list(filter(lambda c: c.get_lname() != "Appleton", sorted_list))
If you want to remove only the first one, use Barmar's answer.
This is the same as a list comprehension, which Python is better at optimizing:
sorted_list = [c for c in sorted_list if c.get_lname() != "Appleton"]
Just Try this:
for x in sorted_list: # Loops through Customers in List
if x.get_lname() == "Appleton": # Check if the last name is Apple
sorted_list.remove(x) # Remove each from the list, Pretty self explanatory
else: # you only want to print if the last name is not APppleton
print(x.get_lname(), x.get_fname(), x.get_gender(), x.get_age())
.remove removes an object from a list so you do not need to track the index of the loop.
Read this w3schools tutorial for more list operations
Related
My __repr__ method works fine using objects created in it's class, but with objects that were created with the help of importing a library and using methods from it, it only represented the memory address...
from roster import student_roster #I only got the list if students from here
import itertools as it
class ClassroomOrganizer:
def __init__(self):
self.sorted_names = self._sort_alphabetically(student_roster)
def __repr__(self):
return f'{self.get_combinations(2)}'
def __iter__(self):
self.c = 0
return self
def __next__(self):
if self.c < len(self.sorted_names):
x = self.sorted_names[self.c]
self.c += 1
return x
else:
raise StopIteration
def _sort_alphabetically(self,students):
names = []
for student_info in students:
name = student_info['name']
names.append(name)
return sorted(`your text`names)
def get_students_with_subject(self, subject):
selected_students = []
for student in student_roster:
if student['favorite_subject'] == subject:
selected_students.append((student['name'], subject))
return selected_students
def get_combinations(self, r):
return it.combinations(self.sorted_names, r)
a = ClassroomOrganizer()
# for i in a:
# print(i)
print(repr(a))
I tried displaying objects that don't rely on anther library, and they dispayed properly.
The issue I was facing was linked to me not understanding the nature of the object. itertools.combinations is an iterable, and in order to represent the values stored I needed to either:
unpack it inside a variable like:
def get_combinations(self, r):
*res, = it.combinations(self.sorted_names, r)
return res
Iter through it inside a loop and leave the original code intact like
for i in a.get_combinations(2):
print(i)
I prefer the second solution
def cinput():
temp = []
print ("Input name: ")
temp.append(input())
print ("Input about: ")
temp.append(input())
return temp
class Person:
def __init__(self,Name,About):
self.Name = Name
self.About = About
new_friend = Person(cinput())
how to make this code work?without changes in class Person
You can use unpacking:
new_friend = Person(*cinput())
This is roughly equivalent to
temp_input = cinput()
new_friend = Person(temp_input[0], temp_input[1])
That is, * "unpacks" the list (or iterables) and seperately use each element as an argument.
I have a simple python code for data cleaning. This code imports data from Excel that has the format like this:
product cost used_by prime
name price gender yes or no
name price gender yes or no
... and so on
afterward I will get a mylist using the class function that looks something like this:
mylist = [Item(comic,20.0,male,yes),
Item(paint,14.0,male,no),
Item(pen,5.0,female,nan),
Item(phone case,9.0,nan,no),
Item(headphone,40.0,male,yes),
Item(coat,nan,male,no),
Item(comic,15.0,male,yes),
Item(nan,15.0,male,no)
... and so on]
and after all the filter and cleaning I will get a result that looks like this:
result = [Item(comic,20.0,male,yes),
Item(underwear,15.0,male,yes),
Item(comic,15.0,male,yes)
...
Item(underwear,15.0,male,yes),
...and so on]
Here is the code I got so far:
import os
import pandas as pd
import math
cwd = os.path.abspath('')
files = os.listdir(cwd)
df = pd.DataFrame()
for file in files:
if file.endswith('.XLSX'):
df = df.append(pd.read_excel(file), ignore_index=True)
df = df.where(df.notnull(), None)
array = df.values.tolist()
print(array)
class Item():
def has_all_properties(self):
return bool(self.__name and not math.isnan(self.__cost) and self.__gender and self.__prime)
def clean(self):
return bool(self.__name and self.__cost <=20 and self.__gender == "male" and self.__prime == "yes")
def __init__(self, name, cost, gender, prime):
self.__name = name
self.__cost = cost
self.__gender = gender
self.__prime = prime
def __repr__(self):
return f"Item({self.__name},{self.__cost},{self.__gender},{self.__prime})"
def __tuple__(self):
return self.__name, self.__cost, self.__gender, self.__prime
mylist = [Item(*k) for k in array]
filtered = filter(Item.has_all_properties, mylist)
clean = filter(Item.clean, filtered)
result = list(clean)
t_list = [obj.__tuple__() for obj in result]
print(t_list)
output = pd.DataFrame(t_list, columns =['name', 'cost', 'gender', 'prime'])
print(output)
output.to_excel('clean_data.xlsx', index = False, header = True)
In the result, there are two type of repetitive data, one is like the underwear which have two exact same lines; and the other one is like the comic, with different cost values.
So what I want to do is remove one of the line that are exact the same for case one and keep the line that has the smaller cost value for case 2.
So for case two, I am think of reading the product to identify if they are the same and if they are I then compare their cost and keep the one with smaller value. But I am not sure if that is the right way of doing it.
I am aware that using pandas all the way work but I wish to explore the class function and use the customized data frame.
Can you suggest how to do this?
You could use a Set instead of a list, i.e., changing myList = [Item(*k) for k in array] to mySet = {Item(*k) for k in array}.
Sets do not allow duplicates.
EDIT
Make sure to include implementations of __hash__ and __eq__ in your Item class so that set can know how to determine whether an item has a duplicate.
In your case, the __eq__ would look something like the following:
def __eq__(self, other):
self.__name == other.__name
self.__cost == other.__cost
self.__gender == other.__gender
self.__prime == other.__prime
I have a class (Student) with different attributes, such as studentId, address, and courses. My str method for the class returns all the information that the user put in. However, for the attributes that are lists, such as courses, the location of the information is printed out instead of the actual information. Here is the code (sorry it's a little long, there's a bunch of classes):
class Person:
__name = None
__age = None
__address = None
def __init__(self, name, age=0, address=None):
self.set_name(name)
self.set_age(age)
self.set_address(address)
def __str__(self):
return 'Name: ' + self.__name + '\n' + \
'Age: ' + str(self.__age) + '\n' + \
'Address: ' + str(self.__address)
def set_name(self, name):
self.__name = name
def get_name(self):
return self.__name
def set_age(self, age):
self.__age = age
def get_age(self):
return self.__age
def set_address(self, address):
self.__address = address
def get_address(self):
return self.__address
class Student(Person):
def __init__(self, name, studentID= None, age= 0, address= None):
super(Student, self).__init__(name, age, address)
self.set_studentID(studentID)
self.__courses =[]
def __str__(self):
result = Person.__str__(self)
result += '\nStudent ID:' + self.get_studentID()
for item in self.__courses:
result += '\n ' + str(item)
return result
def set_studentID(self, studentID):
if isinstance(studentID, str) and len(studentID.strip()) > 0:
self.__studentID = studentID.strip()
else:
self.__studentID = 'NA'
def get_studentID(self):
return self.__studentID
def add_course(self, course):
print('in add_course')
self.__courses.append(course)
def get_courses(self):
for i in range(len(self.__courses)):
return self.__courses[i]
class Course:
__courseName = None
__dept = None
__credits = None
def __init__(self, courseName, dept= 'GE', credits= None):
self.set_courseName(courseName)
self.set_dept(dept)
self.set_credits(credits)
def __str__(self):
return self.get_courseName() + '/' + self.get_dept() + '/' + str(self.get_credits())
def set_courseName(self, courseName):
if isinstance(courseName, str) and len(courseName.strip()) > 0:
self.__courseName = courseName.strip()
else:
print('ERROR: Name must be a non-empty string')
raise TypeError('Name must be a non-empty string')
def get_courseName(self):
return self.__courseName
def set_dept(self, dept):
if isinstance(dept, str) and len(dept.strip()) > 0:
self.__dept = dept.strip()
else:
self.__dept = "GE"
def get_dept(self):
return self.__dept
def set_credits(self, credits):
if isinstance(credits, int) and credits > 0:
self.__credits = credits
else:
self.__credits = 3
def get_credits(self):
return self.__credits
students = []
def recordStudentEntry():
name = input('What is your name? ')
age = input('How old are you? ')
studentID= input('What is your student ID? ')
address = input('What is your address? ')
s1 = Student(name, studentID, int(age), address)
students.append(s1)
s1.add_course(recordCourseEntry())
print('\ndisplaying students...')
displayStudents()
print()
def recordCourseEntry():
courses = []
for i in range(2):
courseName = input('What is the name of one course you are taking? ')
dept = input('What department is your course in? ')
credits = input('How many credits is this course? ')
c1 = Course(courseName, dept, credits)
print(c1)
courses.append(c1)
displayCourses(courses)
return courses
def displayCourses(courses):
print('\ndisplaying courses of student... ')
for c in range(len(courses)):
print(courses[c])
def displayStudents():
for s in range(len(students)):
print()
print(students[s])
recordStudentEntry()
This is how the code above prints out the 'displaying students...' part:
displaying students...
Name: sam
Age: 33
Address: 123 st
Student ID:123abc
[<__main__.Course object at 0x000002BE36E0F7F0>, <__main__.Course object at
0x000002BE36E0F040>]
I know that it is printing out the location because I need to index into the list. However, the length of the list will be different every time. Normally if I wanted to index into a list, for example, to print a list of names, I would do:
listOfNames = ['sam', 'john', 'sara']
for i in range(len(listOfNames)):
print(listOfNames[i])
or
listOfNames = ['sam', 'john', 'sara']
for i in listOfNames:
print(i)
(not sure what if any difference there is between the 2 ways since they both print out the same way:)
sam
john
sara
How can I write something like the indexing into a list technique shown here in my str method for my class so that it prints the information and not the location?
It would be good to keep to the standard conventions for Python, such as naming
private attributes for objects with single underscores, not double underscores.
The latter are reserved for Python "internal" attributes and methods.
Also, it is convention to use object attributes for objects with get/set methods,
not class attributes. This will make it easier to inspect your objects, while
still maintaining data hiding. Example:
class Course:
def __init__(self, courseName, dept= 'GE', credits= None):
self._courseName = None
self._dept = None
self._credits = None
self.set_courseName(courseName)
...
Your question about why the courses don't print out the way you expected
is rooted in a programming error with the way you programmed the recording
of courses. In recordCourseEntry(), you record two courses and put them
in a list. However, you pass that to your Student object using a method
intended for one course at a time. My suggested fix would be:
...
# s1.add_course(recordCourseEntry())
courses = recordCourseEntry()
for course in courses:
s1.add_course(course)
...
This will probably be enough to get you going. An example output I got was:
Name: Virtual Scooter
Age: 33
Address: 101 University St.
Student ID:2021
ff/GE/3
gg/GE/3
I have two classes (Student and Course). I'm trying to write a method for the Course class that will remove a given student from a course. However, there's a problem when I run
self.students.remove(student) in the method. The error tells me that student is not in the students list. Printing the students list I don't actually see the values, but instead I see a reference to it:
print(self.students)
> [<data.Student object at 0x7fc9980334f0>, <data.Student object at 0x7fc998033580>, <data.Student object at 0x7fc9980428b0>, <data.Student object at 0x7fc998042a00>]
However, if I select a specific student at an index then I'm able to see the actual data.
print(self.students[0])
> 2020411:King,Maha
Why is this happening when trying to print the students attribute?
Code if needed:
from copy import deepcopy
class Student:
def __init__(self, sid, last, first):
self.sid = sid
self.last = last
self.first = first
def __str__(self):
return '{}:{},{}'.format(self.sid, self.last, self.first)
def __repr__(self):
return '{}:{},{}'.format(self.sid, self.last, self.first)
class Course:
def __init__(self, crn, students):
self.crn = crn
self.students = deepcopy(students)
def key(self):
return self.crn
def is_empty(self):
return len(self.students) == 0
def get_student(self, student_key):
for student in self.students:
if student.key() == student_key:
return deepcopy(student)
return None
def __contains__(self, student):
for i in self.students:
if student.key() == i.key():
return True
break
return False
def register(self, student):
if student not in self:
self.students.append(deepcopy(student))
return
def drop(self, student):
s = None
if student in self:
s = deepcopy(student)
self.students.remove(student)
return s
student1 = Student(2020411, 'King', 'Maha')
student2 = Student(2019399, 'Hess', 'Alvin')
student3 = Student(2020301, 'Chin', 'Yu')
student4 = Student(2019111, 'Hay', 'Ria')
student_list = [student1, student2, student3]
course1 = Course('CP104', student_list)
removed_student = course1.drop(student2)
The issue with deepcopy() is that it creates an entirely new object that has the same attributes as the original one, yet they are not equal. For list.remove(), this compares the reference to check if the actual object exists. In your case, you are trying to remove an object that is not in the list.
Instead of removing it, if you want to return the student, use list.pop().
def drop(self, student):
for i, s in enumerate(self.students):
if s.sid == student.sid :
return self.students.pop(i)
As a side note, it will be easier to do operations if Course.students is a dictionary such that:
self.students = {
`sid1`: student1,
`sid2`: student2,
# etc
}
EDIT: Alternatively, implement __eq__() in Student so that list.remove() will work.
def __eq__(self, other):
return self.sid == other.sid and self.first == other.first and self.last == other.last