Reputation: 17
How can I convert a CSV file of names with 3 pieces of data (in adjacent columns) into variables of a created class that takes the adjacent pieces of data as arguments?
The code below appears to create the objects but the list is of object locations and not the names as I would prefer.
Can anyone help?
class Teams(object):
def __init__(self, TeamName, FT, FG, Three):
self.TeamName = TeamName
self.FT = FT
self.FG = FG
self.Three = Three
North_Carolina = Teams("North Carolina", .643,.458,.371)
print North_Carolina.TeamName, North_Carolina.FT, North_Carolina.FG,North_Carolina.Three
## works fine but manually typed data
def getData(fname): ##fname is a csv file with 4 columns and 348 rows. One header row.
Data = open(fname, 'r')
TeamList = []
#print Data.read() ## prints a list with comma separated values and line return
for line in Data:
info = line.split(",") ##info is a list of lists of Data with Team,FT,FG,3P
name = info[0]
FT = info[1]
FG = info[2]
Three = info[3]
newTeam = (name, FT, FG, Three)
newTeam2 = Teams(newTeam[0],newTeam[1],newTeam[2],newTeam[3]) ##newTeam2 is an object.
TeamList.append(newTeam2)
print TeamList[1].TeamName ##prints North Carolina list[0] print header
print TeamList #prints list of object locations but not objects themselves
getData("Stats 01-04-2013.csv")
First print
statement prints:
North Carolina
Second print
statement prints:
[<__main__.Teams object at 0x02953230>, <__main__.Teams object at 0x029532B0>, etc etc
Upvotes: 0
Views: 2387
Reputation: 123423
How class instances get displayed is controlled by giving them custom __str__()
and/or __repr__()
conversion methods. Here's one way your code could be written to do that and also make it follow many of the recommendations in the PEP 8 Style Guide. You may also be able improve or simplify it even more by using the csv
module to read the file, as already suggested by others, but I'll focus mostly on the code shown in your question.
class Team(object):
def __init__(self, team_name, free_throws, field_goals, three_pointers):
self.team_name = team_name
self.free_throws = free_throws
self.field_goals = field_goals
self.three_pointers = three_pointers
def __repr__(self):
return '{}({!r}, {}, {}, {})'.format(self.__class__.__name__,
self.team_name, self.free_throws,
self.field_goals, self.three_pointers)
def get_data(fname): # fname is a csv file with 4 columns and one header row
teams = []
with open(fname, 'rt') as csv_file:
csv_file.next() # skip header row
for line in csv_file:
info = line.strip().split(",")
teams.append(Team(*info)) # create a Team instance and add it to list
return teams
print get_data("Stats 01-04-2013.csv")
Sample output:
[Team('North Carolina', .643, .458, .371), Team('Michigan', .543, .358, .271)]
Upvotes: 0
Reputation: 365657
The object locations are how the objects are printed out. Strictly speaking, calling str
or repr
on a list
calls repr
on each element on the list. By default, repr(team)
on a Teams
object will return something like <main.Teams object at 0x02953230>
. You can override that by defining a __repr__
method on the Teams
class. However, this doesn't sound like what you want here.
If TeamList
is a list of Teams
objects, and you want to turn it into a list of their TeamName
members, you just use a list comprehension
to convert it:
TeamNameList = [team.TeamName for team in TeamList]
Note that this still isn't going to be exactly you want to print out:
>>> print TeamNameList
['North Carolina', 'South Carolina', 'West Carolina', 'East Carolina', 'Underground Carolina', 'Cloud-level Carolina', 'Past Carolina', 'Future Carolina'] # those are all states, right?
You probably want something like this:
>>> print ', '.join(TeamNameList)
North Carolina, South Carolina, West Carolina, East Carolina, Underground Carolina, Cloud-level Carolina, Past Carolina, Future Carolina
From your comments:
there is a later function simGame(teamA, Teamb)
I see the instances, however I am not sure how to call them in the second function. I had planned on calling them by name. In the example I gave, North_carolina can be passed to the later (not shown function) and calculations can be run on the data
I think I understand what you want here. You want to be able to simulate a game between North Carolina and South Carolina, when all you have is the TeamList
.
To do that, you probably want to create a dict
, mapping the names to the Teams
objects, like this:
TeamDict = {team.TeamName: team for team in TeamList}
Now, you can do things like this:
simGame(TeamDict['North Carolina'], TeamDict['South Carolina'])
Now, simGame
is going to get the North Carolina and South Carolina instances of the Teams
class as its teamA
and teamB
arguments, so it can do things like:
def simGame(teamA, teamB):
scoreA = int(teamA.FT * 1 * 20) + int(teamA.FG * 2 * 40) + int(teamA.Three * 3 * 10)
scoreB = int(teamB.FT * 1 * 20) + int(teamB.FG * 2 * 40) + int(teamB.Three * 3 * 10)
if scoreA > scoreB:
print 'Home team {} beats visitor {} by {}!'.format(teamA.TeamName,
teamB.TeamName,
scoreA - scoreB)
else:
print 'Visitors {} win by {} over {} at home!'.format(teamB.TeamName,
scoreB - scoreA,
teamA.TeamName)
Is that what you want?
Some additional comments:
You can also do the same thing as the list comprehension using map
, which avoids having to write team
twice, but it also means you can't use normal expression syntax:
TeamNameList = map(operator.attrgetter('TeamName'), TeamList)
Or you can use map
together with lambda
to get back the expression syntax back… and the repeat of team
:
TeamNameList = map(lambda team: team.teamName, TeamList)
But for cases like this, the list comprehension is generally considered the pythonic way to do it. (Also, it doesn't change if you go to Python 3, whereas map
changes from a list
to an iterator, which means print TeamNameList
will give you something like <builtins.map at 0x108022350>
… But ', '.join(TeamNameList)
will still work.)
As a side note, in standard (PEP 8) Python style, usually only classes are in TitleCase like this, and variables and attributes are in lower_case. If you really like CamelCase, you can get away with lowerFirstCamelCase, but using TitleCase will throw off people reading your code, who will immediately try to figure out where the TeamName
class is defined. Also see the comments from bvukelic.
As another side note, you seem to be doing a lot of repeated code, and I'm not sure why:
name = info[0]
FT = info[1]
FG = info[2]
Three = info[3]
newTeam = (name, FT, FG, Three)
You're just copying info
to newTeam
; why add all those intermediate variables that never get used again?
newTeam2 = Teams(newTeam[0],newTeam[1],newTeam[2],newTeam[3])
You could replace all of that with just:
newTeam2 = Teams(info[0],info[1],newTeam[2],newTeam[3])
Or even:
newTeam2 = Teams(*info)
If you have a need for the separate variables somewhere that you haven't shown us, that's fine, but you still can't possibly need newTeam
; just do this:
newTeam2 = Teams(name, FT, FG, Three)
Upvotes: 0
Reputation: 627
I'd use the csv module to do something like this, largely based on the examples in the module documentation. Tweaks may be needed, this is just off the top of my head, but I always use the relevant when working with these types of files.
import csv
with open('teams.csv', 'rb') as csvfile:
teamreader = csv.reader(csvfile)
for row in teamreader:
newTeam = Teams(row[0], row[1], row[2], row[3])
teamList.append(newTeam)
Upvotes: 1
Reputation:
Just to cover another interpretation of the question:
class Teams(object):
def __init__(self, TeamName, FT, FG, Three):
self.TeamName = TeamName
self.FT = FT
self.FG = FG
self.Three = Three
def __str__(self):
return str(self.TeamName)
def __repr__(self):
return self.__unicode__()
Whenever you print the object, it will be shown as TeamName
Upvotes: 0