Python: referencing class object list of lists - python

I am fairly new to python. I have tried to define a class, I then want to create an instance from a file, then refer to specific pieces of it, but cannot seem to. This is Python 3.3.0
Here's the class....
class Teams():
def __init__(self, ID = None, Team = None, R = None, W = None, L = None):
self._items = [ [] for i in range(5) ]
self.Count = 0
def addTeam(self, ID, Team, R=None, W = 0, L = 0):
self._items[0].append(ID)
self._items[1].append(Team)
self._items[2].append(R)
self._items[3].append(W)
self._items[4].append(L)
self.Count += 1
def addTeamsFromFile(self, filename):
inputFile = open(filename, 'r')
for line in inputFile:
words = line.split(',')
self.addTeam(words[0], words[1], words[2], words[3], words[4])
def __len__(self):
return self.Count
Here's the code in Main
startFileName = 'file_test.txt'
filename = startFileName
###########
myTestData = Teams()
myTestData.addTeamsFromFile(startFileName)
sample data in file
100,AAAA,106,5,0
200,BBBB,88,3,2
300,CCCC,45,1,4
400,DDDD,67,3,2
500,EEEE,90,4,1
I think I am good to here (not 100% sure), but now how do I reference this data to see... am i not creating the class correctly? How do I see if one instance is larger than another...
ie, myTestData[2][2] > myTestData[3][2] <----- this is where I get confused, as this doesn't work

Why don't you create a Team class like this :
class Team():
def __init__(self, ID, Team, R=None, W = 0, L = 0)
# set up fields here
Then in Teams
class Teams():
def __init__(self):
self._teams = []
def addTeam (self, ID, Team, R=None, W = 0, L = 0)
team = Team (ID, Team, R=None, W = 0, L = 0)
self._teams.append (team)
Now If i got it right you want to overwrite the > operator's behaviour.
To do that overload __gt__(self, other) [link]
So it will be
class Team ():
# init code from above for Team
def __gt__ (self, otherTeam):
return self.ID > otherTeam.ID # for example
Also be sure to convert those strings to numbers because you compare strings not numbers. Use int function for that.

The immediate problem you're running into is that your code to access the team data doesn't account for your myTestData value being an object rather than a list. You can fix it by doing:
myTestData._items[2][2] > myTestData._items[3][2]
Though, if you plan on doing that much, I'd suggest renaming _items to something that's obviously supposed to be public. You might also want to make the addTeamsFromFile method convert some of the values it reads to integers (rather than leaving them as strings) before passing them to the addTeam method.
An alternative would be to make your Teams class support direct member access. You can do that by creating a method named __getitem__ (and __setitem__ if you want to be able to assign values directly). Something like:
def __getitem__(self, index):
return self._items[index]
#Aleksandar's answer about making a class for the team data items is also a good one. In fact, it might be more useful to have a class for the individual teams than it is to have a class containing several. You could replace the Teams class with a list of Team instances. It depends on what you're going to be doing with it I guess.

Related

Splitting Data in Python?

it does not work. I want to split data as in code in lines attribute.
class movie_analyzer:
def __init__(self,s):
for c in punctuation:
import re
moviefile = open(s, encoding = "latin-1")
movielist = []
movies = moviefile.readlines()
def lines(movies):
for movie in movies:
if len(movie.strip().split("::")) == 4:
a = movie.strip().split("::")
movielist.append(a)
return(movielist)
movie = movie_analyzer("movies-modified.dat")
movie.lines
It returns that:
You can use #property decorator to be able to access the result of the method as a property. See this very simple example of how this decorator might be used:
import random
class Randomizer:
def __init__(self, lower, upper):
self.lower = lower
self.upper = upper
#property
def rand_num(self):
return random.randint(self.lower, self.upper)
Then, you can access it like so:
>>> randomizer = Randomizer(0, 10)
>>> randomizer.rand_num
5
>>> randomizer.rand_num
7
>>> randomizer.rand_num
3
Obviously, this is a useless example; however, you can take this logic and apply it to your situation.
Also, one more thing: you are not passing self to lines. You pass movies, which is unneeded because you can just access it using self.movies. However, if you want to access those variables using self you have to set (in your __init__ method):
self.movielist = []
self.movies = moviefile.readlines()
To call a function you use movie.lines() along with the argument. What you are doing is just accessing the method declaration. Also, make sure you use self as argument in method definitions and save the parameters you want your Object to have. And it is usually a good practice to keep your imports at the head of the file.
import re
class movie_analyzer:
def __init__(self,s):
for c in punctuation:
moviefile = open(s, encoding = "latin-1")
self.movielist = []
self.movies = moviefile.readlines()
#property
def lines(self):
for movie in self.movies:
if len(movie.strip().split("::")) == 4:
a = movie.strip().split("::")
self.movielist.append(a)
return self.movielist
movie = movie_analyzer("movies-modified.dat")
movie.lines()

dynamic instances of a class object overwriting each other

I have a simple class that stores simple data. The class is as follows.
class DataFormater:
def __init__(self, N, P, K, price):
self.N = N
self.P = P
self.K = K
self.price = price
The code that calls this class is
from DataFormater import DataFormater
#global variables
ObjectList = [0,1,2,3,4,5,6,7,8,9,10,
11,12,13,14,15,16,17,18,19,20,
21,22,23,24,25,26,27,28,29,30,
31,32,33,34,35,36,37,38,39,40,
41,42,43,44,45,46,47,48,49,50]
ObjectListCounter = 0
# main
print "enter you N-P-K values, followed by a coma, then the price"
print "example ----> 5 5 5 %50 "
print "return as many values as you want to sort, then enter, 'done!' when done."
while True:
RawData = raw_input()
if RawData == 'done!':
break
else:
ObjectList[ObjectListCounter] = DataFormater
ObjectList[ObjectListCounter].N = int(RawData[0])
# very simple test way of putting first indice in ObjectList[ObjectListCounter].N
ObjectListCounter += 1
print ObjectList[0].N
print ObjectList[1].N
My idea is that ObjectList[0] would create that object '1' that I could call with 1.N
But, when I call these, it seems that I have overwritten the previous instances.
this is what prints...
return as many values as you want to sort, then enter, 'done!' when done.
12
1
done!
1
1
Thanks so much! And I know that my post is messy, I don't exactly know how to make it more "pretty"
So, it looks like you are assigning the actual class (instead of an instance of the class) in your loop. Where you do this:
ObjectList[ObjectListCounter] = DataFormater
I think what you actually want is this
ObjectList[ObjectListCounter] = DataFormater(...insert args here....)
EDIT to address the comments:
Your class init method looks like this:
def __init__(self, N, P, K, price):
That means that to create an instance of your class, it would look like this:
my_formater = DataFormater(1, 2, 3, 4)
You would then be able to access my_formater.N which would have a value of 1.
What you are trying to do instead is access a CLASS level attribute, DataFormater.N. This is generally used in situations where you have a constant variable that does not change between instances of the class. For example:
class DataFormater():
CONSTANT_THING = 'my thing that is always the same for every instance'
You would then be able to access that variable directly from the class, like this:
DataFormater.CONSTANT_THING
I hope that clears things up.

Refactoring For-Loops in Python

I am currently working on an iTunes data program that's cycling constantly through a user's library to get statistics about one's library.
returns
I have a few code snippets like these:
def numArtist(self):
num = 0
for song in self.allSongs:
tempList = []
if song.artist not in tempList:
tempList.append(song.artist)
num += 1
return num
def getAlbumNames(self):
albums = []
for song in self.allSongs:
if song.album not in albums:
albums.append(song.album)
return albums
Where the main for loop body is repeated:
for song in self.allSongs: # same for-loop condition
# different for-loop body
Is there a way to refactor methods like these, where I have the same for loop conditions but with different body definitions?
I have a quite a few methods with the same for-loop, so I'd like to find a way to decrease the complexity and redundancy of my code.
Just for reference, all Song objects have attributes - artist, album (name),genre, etc - that I'm using to get my data.
Use set comprehensions and len to simplify each of them:
def numArtist(self):
return len({song.artist for song in self.allSongs})
def getAlbumNames(self):
return {song.album for song in self.allSongs}
To make it more generic, you could write a method that takes a lambda and use that to filter the property out of each song:
def uniqueProps(self, fxn):
return {fxn(song) for song in self.allSongs}
def getAlbumNames(self):
return self.uniqueProps(lambda song: song.album)
You can use set comprehensions for both snippets, if that counts as a valid "For-Loop refactoring":
artist_count = len({song.artist for song in self.allSongs})
album_names = set({song.album for song in self.allSongs})
Generic version using getattr
get_values = lambda objs, attr: {getattr(obj, attr) for obj in objs
attributes = 'artist', 'album'
values = [get_values(self.allSongs, name) for name in attributes]
artists, albums = values
artist_count = len(artists)
Generic version using lambda
get_artist = lambda song: song.artist
get_album = lambda song: song.album
getters = get_artist, get_album
values = [
{func(song) for song in self.allSongs}
for getter in getters
]
artists, albums = values
artist_count = len(artists)
Generic version using property
# If `song` is an instance of the `Song` class and both `artist` and
# `album` are properties defined on the class, it's also possible to
# directly use the property getter (`property.fget`) to avoid defining
# the lambdas manually:
get_artist = Song.artist.fget
get_album = Song.album.fget
... # <same as above>
If the contents of your allSongs list are immutable - which I suspect they are - you can convert your lists to sets and back to lists again - or use set comprehension - to get rid of duplicates. Then your functions can be greatly simplified like so:
def numArtist(self):
return len({song.artist for sing in self.allSongs})
def getAlbumNames(self):
return list({song.album for song in self.allSongs})
If you're not sure if the song objects are mutable or not, try this out anyway. If they're mutable objects you'll get an exception like:
TypeError: unhashable type: ...
You could try to create the generators, that produces the value of song attributes. Let me give you an example:
def gen_attr(songs, attr_name):
for song in songs:
yield getattr(song, attr_name)
class Song(object):
def __init__(self, name, artist):
self.name = name
self.artist = artist
class Album(object):
def __init__(self, songs_list):
self.songs_list = songs_list
def allSongs(self):
return self.songs_list
s = Song('Ahoy', 'Pirate')
s1 = Song('Bye', 'My Son')
s2 = Song('Ahoy', 'Captain')
a = Album([s, s1])
Now if you want to get all of the song names, u can use:
song_names = list(gen_attr(a.allSongs(), 'name'))
print(song_names) # ['Ahoy', 'Bye', 'Ahoy']
For non-repeated song names you would use:
song_names = list(set(gen_attr(a.allSongs(), 'name')))
print(song_names) # ['Ahoy', 'Bye']
To count the non-repeated artists names, you would use:
artists = len(set(gen_attr(a.allSongs(), 'artist')))
And to create the list of artists, just go for:
artists = list(gen_attr(a.allSongs(), 'artist'))
print(artists) # ['Pirate', 'My Son', 'Captain']

Method changes all instances of class inside list

In my script I have these classes:
class action:
def __init__(self,ac_type,ac_date):
self.ac_type = ac_type
self.ac_date = ac_date
class user:
actions = []
def __init__(self,i_id):
self.ivi_id = i_id
def add(self,act):
self.actions.append(act)
def get_len(self):
return len(self.actions)
I want to create list of "user" elements and add to some of theme actions. I do this in the following way:
for i in range(len(data_queue)):
ind = users_id.index(data_queue[i].i_id);
act = action(0,data_queue[i].added)
users[ind].add(act)
But after running this I see that every action from data_queue was added to every user from users. This is wrong! What shall I change?
The code is using class attribute which is shared by all instances of the class and the class itself.
Use an instance attribute instead:
class user:
def __init__(self,i_id):
self.ivi_id = i_id
self.actions = []
def add(self,act):
self.actions.append(act)
def get_len(self):
return len(self.actions)
BTW, the code is using an index to iterate the sequence data_queue. Just iterate the sequence unless you really need the index.
for queue in data_queue:
ind = users_id.index(queue.i_id)
act = action(0, queue.added)
users[ind].add(act)

How to use filter in Python with a function which belongs to an object which is an element on the list being filtered?

To be specific in my case, the class Job has a number of Task objects on which it operates.
import tasker
class Job(object):
_name = None
_tasks = []
_result = None
def __init__(self, Name):
self._name = Name
def ReadTasks(self):
# read from a Json file and create a list of task objects.
def GetNumTasks(self):
return len(self._tasks)
def GetNumFailedTasks(self):
failTaskCnt = 0
for task in self._tasks:
if task.IsTaskFail():
failTaskCnt += 1
To make GetNumFailedTasks more succinct, I would like to use a filter, but I am not sure what is the correct way to provide filter with IsTaskFail as the first parameter.
In case, this is a duplicate, please mark it so, and point to the right answer.
You can use a generator expression with sum:
failTaskCnt = sum(1 for task in self._tasks if task.IsTaskFail())

Categories