creating a list from a text file in python - python

I have a text file that looks something like this where the first column is a student's name, the second column is the number of credits, and the third is the number of points (grade times hours).
john 5 15
bill 9 30
ted 7 22
I want to create a class that extracts the relevant information and calculates gpa.
class Student:
def __init__(self, name, hours, qpoints):
self.name = name
self.hours = float(hours)
self.qpoints = float(qpoints)
def getName(self):
return self.name
def getHours(self):
return self.hours
def getQPoints(self):
return self.qpoints
def gps(self):
return self.qpoints/self.hours
used to make extract the data (based on the fact that there is a tab between each piece of information)
def makeStudent(info):
name, hours, qpoints = info.split("\t")
return Student(name, hours, qpoints)
here I use a for loop to create a list based on the data in the text file by appending the relevant information from each line to the list
def readStudents(filename):
infile = open(filename, 'r')
students = []
for line in infile:
students.append(makeStudent(line))
infile.close()
return students
the problem is that I get this error:
[<__main__.Student object at 0x01FA4AD0>, <__main__.Student object at 0x01FA4AF0>,
<__main__.Student object at 0x01FA4B10>, <__main__.Student object at 0x01FA4B50>,
<__main__.Student object at 0x01FA4B30>]
Any ideas on why this is happening?

This is not an error. It is regular output. You should override the __str__ and __repr__ methods of the Student class, to tell python how to print Student objects.
Some help on your code, this is much better:
def readStudents(filename):
with open(filename) as student_file:
return [Student(*line.split()) for line in student_file]

Like everyone says, not an error. Just implement __str__, __repr__ or __unicode__ on your Student class.
However, I have one minor suggestion. You should use the csv module to read your file.
Your readStudents function can also be re-written like this:
def readStudents(filename):
students = []
with open(filename) as infile:
for line in csv.reader(infile, delimiter='\t'): # or excel-tab if you want.
students.append(Student(**line))
return students
Isn't that pretty?
Don't forget to put a import csv at the beginning of your python file!

I strongly suggest using the csv module (import csv): it will do most of the work for you, be more flexible, more readable and less prone to bugs.
Also, to be a little more strict: what is your actual question? I just see a blurb of code and some output you do not understand. That is not an error message, by the way, it is a list of five instances of the Student class. The code seems to work as intended: you parse the file, create students, and then..? What do you want to do with them? The parsing is done, you want to move on to the handling of the data.

You don't get an error, but a list of student objects. What you need to do is implement a __str__ or a __repr__ method: Special method names

Getter and setter methods are usually frowned on unless they are doing something active (over and above just retrieving or setting a value).
import csv
class Student(object):
def __init__(self, name, hours, qpoints):
super(Student,self).__init__()
self.name = str(name)
self.hours = float(hours)
self.qpoints = float(qpoints)
#property
def gpa(self):
return self.qpoints/self.hours
#gpa.setter
def gpa(self, value):
raise SyntaxError('gpa property is read-only; update .qpoints or .hours instead')
def __str__(self):
return "{name:20} {hours:>6.2f} {qpoints:>6.2f} {gpa:>6.2f}".format(name=self.name, hours=self.hours, qpoints=self.qpoints, gpa=self.gpa)
def loadStudents(fname, *args, **kwargs):
with open(fname) as inf:
return [Student(*line) for line in csv.reader(inf, *args, **kwargs)]
def main():
students = loadStudents('students.csv', delimiter='\t')
for s in students:
print s
if __name__=="__main__":
main()
results in
john 5.00 15.00 3.00
bill 9.00 30.00 3.33
ted 7.00 22.00 3.14

Related

Sorting objects using CSV data

Before I start I am noob
So, I made objects using data from a CSV file which was in the form 'doe,john,m,20', which can be seen below:
class FitClinic:
def __init__(self, lname, fname, gender, age):
self.__lname = lname
self.__fname = fname
self.__gender = gender
self.__age = age
def __del__(self):
print("Customer has been deleted")
def set_lname(self):
pass
def get_lname(self):
return self.__lname
def set_fname(self):
pass
def get_fname(self):
return self.__fname
def set_gender(self):
pass
def get_gender(self):
return self.__gender
def set_age(self):
pass
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:]))
What I need to do is sort these objects by the fname attribute which I have no idea how to do, please help, thanks.
You can use sorted with key
sorted_list=sorted(listofcustomers,key=lambda x: x.get_fname())
Refer to Sorting HOW TO
To check the result, you can just print the information with the method you have implemented:
for x in sorted_list:
print(x.get_fname())
In a more complicated situation, advised by #Maurice Reeves, you can also add __str__ and __repr__ methods.
def __str__(self):
str_to_print=f'lname:{self.__lname},'
str_to_print+=f'fname:{self.__fname},'
str_to_print+=f'gender:{self.__gender},'
str_to_print+=f'age:{self.__age}'
return str_to_print
__repr__=__str__
Then you can print by:
for x in sorted_list:
print(x)
Refer to Python doc.
BTW, you can use pandas to load csv file conveniently.
import pandas
csv_pd=pandas.read_csv('fit_clinic_20.csv')
csv_pd.sort_values(by=['fname']) # If fname is the head of your csv file. If not, just add it.
Refer to pandas.DataFrame.sort_values

Adding a list the class

class Course:
'''
A class representing a course offering that includes the following
information about the course: subject, course number, section,
enrolment cap, lecture days, lecture start time, lecture duration
in minutes, and unique student numbers of the students enroled.
If lectures occur on multiple days, they will all start at the same time.
'''
def __init__(self, subject, number, section, cap, days, start_time, dur):
'''
returns a new Course object with no students enroled, given
the subject, number, section, cap, days, start_time, and dur
__init__: Str Nat Nat Nat Str Time Nat -> Course
requires: number is a 3-digit number, section > 0, cap > 0,
days is string containing substrings representing the days
of the week 'M', 'T', 'W', 'Th', 'F', where if the course is
offered on more than one day, the days appear in order and
are separated by a single dash. For example, 'M-T-Th'
indicates the course is offered on Monday, Tuesday, and Th.
'''
self.subject = subject
self.number = number
self.section = section
self.cap = cap
self.days = days
self.start_time = start_time
self.dur = dur
def add_student(self, student_id):
'''
adds a student to the course enrolment if there is room in the course
if the number of students enroled already matches the
enrolment cap, then print the message "Course full"
if the student is already enroled in the course, the there is no
change to the Course object, and the message "Previously enroled"
is printed.
add_student: Course Nat -> None
Effects: Mutates self. May print feedback message.
'''
pass
For the method add_student, how would i implement a list if it not in the init method, (CANT ADD IT TO THE INIT METHOD)? The list need to be connected with the object so later on i can remove students from that list.
You can add it in the __new__ method instead:
class Course:
def __new__(cls, *args, **kwargs):
course = super(Course, cls).__new__(cls)
course.students = []
return course
naive method: try if the member exists, catch the attribute error, and create it if doesn't exist.
def add_student(self, student_id):
try:
self.__list
except AttributeError:
self.__list = []
self.__list.append(student_id)
better use a "getter" instead to make sure the list is created when you access it from whatever method:
def get_list(self):
try:
self.__list
except AttributeError:
self.__list = []
return self.__list
then add_student becomes:
def add_student(self, student_id):
self.get_list().append(student_id)
of course adding it to __init__ is better if you don't have some strange constraints...
You can initialize the list with a property getter, so that it would be initialized whenever it is first accessed:
class Course:
#property
def students(self):
try:
return self._students
except AttributeError:
self._students = []
return self._students
#students.setter
def students(self, value):
self._students = value

Python - Assigning attributes to a class instance using a for loop and a list

Hi folks I am experimenting with Python (I found pygame.org and wanted to play around) and I am trying to read some settings from a configuration file. I want to be able to change stats on the fly. (So if I wanted to change how hard a fighter hits or how fast a wizard runs then I'd be able to do that.) I was hoping to be able to read from a list and create an attribute for each instance in the list basically this:
for stat in Character.stats:
self.stat = parser.get(self.char_class, stat)
What ends up happening is there is an object with an attribute names 'stat' that contains the last value assigned. What I would LIKE to happen is to have an attribute created for each item in the list, and then get assigned the related value from the config file.
here is more code for context:
class Character(object):
stats = ["level_mod",
"power",
"speed",
"hit",
"evade",
"magic",
"stamina",
"magic_defense",
"intelligence"]
def __init__(self, name, rpg_id):
self.name = name
self.rpg_id = rpg_id
self.__setStats()
def __setStats(self):
parser = SafeConfigParser()
parser.read('char_config.cfg')
for stat in Character.stats:
self.stat = parser.get(self.char_class, stat)
Thanks for your time!
You can use, setattr:
for stat in Character.stats:
setattr(self, stat, parser.get(self.char_class, stat))
Or manually access dict
for stat in Character.stats:
self.__dict__[stat] = parser.get(self.char_class, stat))
You want setattr(obj, attrname, value)
You better re-design that part of the game by adding a Stats class.
class Stats:
STATS = ["level_mod",
"power",
"speed",
"hit",
"evade",
"magic",
"stamina",
"magic_defense",
"intelligence"]
def __init__(self, conf_file=None):
self.__stats = {}
if conf_file is not None:
self.loads_stats_from_file(conf_file)
def load_stats_from_file(self, conf_file):
"""
Here add the pairs <stat_name>:<value>
to the self.__stats dict. For that just parse the config
file like before.
"""
pass
def get_stat(self, stat_name):
return self.__stats[stat_name]
def set_stat(self, stat_name, value):
self.__stats[stat_name] = value
Then you can add a Stats instance to your Character.
class Character(object):
def __init__(self, name, rpg_id):
self.stats = Stats("char_config.cfg")
self.name = name
self.rpg_id = rpg_id
This way you improve usability and decouple the Stats and Character logics. And besides, your problem is reduced from "Adding attributes to an object" to "Adding items to a dictionary".

AttributeError: 'list' object has no attribute 'display' in python

Error comes when i call the display function using class object
What should i do to overcome this ??
class A:
def __init__(self, fname, lname, age):
self.fname = fname
self.lname = lname
self.age = age
def disply(self):
fp = open("abc","r")
for lines in fp:
temp = lines.split(", ")[-1]
fp.close()
print a
a = [A("Taylor","Launter",22), A("James","bond",40)]
a.display()
You have a list of instances called a. a isn't the instance/s, it's a list.
You probably meant to do:
for myobject in a:
myobject.disply() # Note "disply" instead of "display"
a = [A("Taylor","Launter",22), A("James","bond",40)]
a.display()
Now a is a list. Lists in python dont have display method.
What you might actually have wanted to do is to invoke display method of the object of A. If that is the case, you might want to do something like this
for currentObject in [A("Taylor","Launter",22), A("James","bond",40)]:
currentObject.display()
Edit Your display method doesnt make any sense to me.

Instantiating a unique object every time when using object composition?

As an example, just a couple of dummy objects that will be used together. FWIW this is using Python 2.7.2.
class Student(object):
def __init__(self, tool):
self.tool = tool
def draw(self):
if self.tool.broken != True:
print "I used my tool. Sweet."
else:
print "My tool is broken. Wah."
class Tool(object):
def __init__(self, name):
self.name = name
self.broken = False
def break(self):
print "The %s busted." % self.name
self.broken = True
Hammer = Tool(hammer)
Billy = Student(Hammer)
Tommy = Student(Hammer)
That's probably enough code, you see where I'm going with this. If I call Hammer.break(), I'm calling it on the same instance of the object; if Billy's hammer is broken, so is Tommy's (it's really the same Hammer after all).
Now obviously if the program were limited to just Billy and Tommy as instances of Students, the fix would be obvious - instantiate more Hammers. But clearly I'm asking because it isn't that simple, heh. I would like to know if it's possible to create objects which show up as unique instances of themselves for every time they're called into being.
EDIT: The kind of answers I'm getting lead me to believe that I have a gaping hole in my understanding of instantiation. If I have something like this:
class Foo(object):
pass
class Moo(Foo):
pass
class Guy(object):
def __init__(self, thing):
self.thing = thing
Bill = Guy(Moo())
Steve = Guy(Moo())
Each time I use Moo(), is that a separate instance, or do they both reference the same object? If they're separate, then my whole question can be withdrawn, because it'll ahve to make way for my mind getting blown.
You have to create new instances of the Tool for each Student.
class Student(object):
def __init__(self, tool):
self.tool = tool
def draw(self):
if self.tool.broken != True:
print "I used my tool. Sweet."
else:
print "My tool is broken. Wah."
class Tool(object):
def __init__(self, name):
self.name = name
self.broken = False
def break(self):
print "The %s busted." % self.name
self.broken = True
# Instead of instance, make it a callable that returns a new one
def Hammer():
return Tool('hammer')
# Pass a new object, instead of the type
Billy = Student(Hammer())
Tommy = Student(Hammer())
I'll try to be brief. Well.. I always try to be brief, but my level of success is pretty much random.randint(0, never). So yeah.
Lol. You even failed to be brief about announcing that you will try to be brief.
First, we need to be clear about what "called into being" means. Presumably you want a new hammer every time self.tool = object happens. You don't want a new instance every time, for example, you access the tool attribute, or you'd always a get a new, presumably unbroken, hammer every time you check self.tool.broken.
A couple approaches.
One, give Tool a copy method that produces a new object that should equal the original object, but be a different instance. For example:
class Tool:
def __init__(self, kind):
self.kind = kind
self.broken = False
def copy(self):
result = Tool(self.kind)
result.broken = self.broken
return result
Then in Student's init you say
self.tool = tool.copy()
Option two, use a factory function.
def makehammer():
return Tool(hammer)
class Student:
def __init__(self, factory):
self.tool = factory()
Billy = Student(makehammer)
I can't think any way in Python that you can write the line self.tool = object and have object automagically make a copy, and I don't think you want to. One thing I like about Python is WYSIWYG. If you want magic use C++. I think it makes code hard to understand when you not only can't tell what a line of code is doing, you can't even tell it's doing anything special.
Note you can get even fancier with a factory object. For example:
class RealisticFactory:
def __init__(self, kind, failurerate):
self.kind = kind
self.failurerate = failurerate
def make(self):
result = Tool(self.kind)
if random.random() < self.failurerate:
result.broken = True
if (self.failurerate < 0.01):
self.failurerate += 0.0001
return result
factory = RealisticFactory(hammer, 0.0007)
Billy = Student(factory.make)
Tommy = Student(factory.make) # Tommy's tool is slightly more likely to be broken
You could change your lines like this:
Billy = Student(Tool('hammer'))
Tommy = Student(Tool('hammer'))
That'll produce a distinct instance of your Tool class for each instance of the Student class. the trouble with your posted example code is that you haven't "called the Tool into being" (to use your words) more than once.
Just call Tool('hammer') every time you want to create a new tool.
h1 = Tool('hammer')
h2 = Tool('hammer')
Billy = Student(h1)
Tommy = Student(h2)
Oh wait, I forgot, Python does have magic.
class Student:
def __setattr__(self, attr, value):
if attr == 'tool':
self.__dict__[attr] = value.copy()
else:
self.__dict__[attr] = value
But I still say you should use magic sparingly.
After seeing the tenor of the answers here and remembering the Zen of Python, I'm going to answer my own dang question by saying, "I probably should have just thought harder about it."
I will restate my own question as the answer. Suppose I have this tiny program:
class Item(object):
def __init__(self):
self.broken = False
def smash(self):
print "This object broke."
self.broken = True
class Person(object):
def __init__(self, holding):
self.holding = holding
def using(self):
if self.holding.broken != True:
print "Pass."
else:
print "Fail."
Foo = Person(Item())
Bar = Person(Item())
Foo.holding.smash()
Foo.using()
Bar.using()
The program will return "Fail" for Foo.using() and "Pass" for Bar.using(). Upon actually thinking about what I'm doing, "Foo.holding = Item()" and "Bar.holding = Item()" are clearly different instances. I even ran this dumpy program to prove it worked as I surmised it did, and no surprises to you pros, it does. So I withdraw my question on the basis that I wasn't actually using my brain when I asked it. The funny thing is, with the program I've been working on, I was already doing it this way but assuming it was the wrong way to do it. So thanks for humoring me.

Categories

Resources