Finding specific item in class-list in Python - python

Im working on a project where I created a class named Image.
def __init__(self, name):
self.name = name
self.pixelArray = None
self.key = None
def setPixelArray(self, pixelArray):
self.pixelArray = pixelArray
def setKey(self, key):
self.key = key
Later on i create a list containing Images. And now I want to in a easy way get a specific item out of that list using the name attribute, image.name. Below you can see me getting the imageid that i want to find in the already created image-list.
split = line.split()
imageid = split[0]
key = split[1]
I have been looking around but not found any example of how to do this without a lot of loops. Is there an easy way to rewrite
if x in myList:
do something with x:

Related

How to Reference List of Dicitionaries from One Class to Another?

I am currently working on an inventory system and trying to implement a class that serves as a "fast fact finer" for my database, which is managed by my Database Management class.
The csv file looks like this:
I have the following code:
class DataBase_Management(object):
def __init__(self):
self.result = []
def make_dict_items(self):
with open("Items2.csv") as fp:
reader = csv.reader(fp)
labels = next(reader, None)
result = []
for row in reader:
if row:
row[0] = int(row[0])
row[1] = float(row[1])
row[2] = int(row[2])
pairs = zip(labels, row)
self.result.append(dict(pairs))
def get_listofdicts(self):
return self.result
The above class manages the list of dicts, through several methods that add items, delete items, etc (not included for simplicity to question).
I also have this class I am working on:
class DB_Fact_Finder():
def __init__(self, management):
self.management = management
def return_item_num(self, item):
items = self.management.get_listodicts()
return items[item]["Item #"]
def return_price(self, item):
items = self.management.get_listodicts()
return items[item]["Price "]
def return_name(self, item):
items = self.management.get_listodicts()
return self.result[item]["Name"]
def return_qnty(self, item):
items = self.management.get_listodicts()
return self.result[item]["Quantity"]
Basically, I want the DB_Fact_Finder class to be able to look at the self.result list of dictionaries defined in the management class, and be able to find specific items such as price, quantity, etc. I tried to implement this in the above code, but feel like I am missing something key to be able to reference the self.result from another class?. How would I do this?
Usage: create instance of DataBase_Management, populate results list, pass to DB_Fact_Finder
db_management = DataBase_Management()
db_management.make_dict_items()
db_fact_finder = DB_Fact_Finder(db_management)
# example usage:
price_1 = db_fact_finder.return_price(0)
print(item_num)
# 5.99
Note there are some problems with the methods of DB_Fact_Finder.
This does not work:
def return_name(self, item):
items = self.management.get_listodicts()
return self.result[item]["Name"]
There will not be a self.results property of a DB_Fact_Finder object as you have the class defined. Same issue exists in def return_qnty
The first two methods are calling self.management correctly.

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']

Calling Object inside of List

class MySong:
_songTitle = "Song Title"
_artistName = "Artist Name"
_likeIndicator = -1
def setTitleAndArtist(self, songTitle, artistName):
self._songTitle = songTitle
self._artistName = artistName
def setLike(self, likeIndicator):
self._likeIndicator = likeIndicator
def undoSetLike(self, songTitle):
Null
def getTitle(self):
return self._songTitle
def getArtist(self):
return self._artistName
def getLikeIndicator(self):
return self._likeIndicator
class MyPlaylist:
_mySongs = []
def add(self, song):
self._mySongs.append(song)
def showTitles(self):
index = 0
titlesList = []
while index != len(self._mySongs):
titlesList.append(self._mySongs[index].getTitle())
index = index + 1
return titlesList
def remove(self):
remindex = 0
while remindex != len(self._mySongs):
if (self._mySongs[index].getTitle()) == remChoice :
return("Song FOUND debug!")
self._mySongs.remove(index)
else:
remindex = remindex + 1
return("Song NOT FOUND debug!")
def getMySong(self):
Null
There is a list of song objects inside of _mySongs = []. I'm trying to remove one, based on the title variable of that object.
In a separate (unshown) part of the program, the user is asked to enter the title of the song they want removed as a string. This is saved as remChoice.
I'm not entirely sure how to remove the song based on the title.
I've tried for a while to get it going, obviously we find the index of the song in the list by matching it to the title (by calling the getTitle method), then removing that index when it's found.
This isn't working. Where am I going wrong?
If you want to delete an item from a list knowing it's index use:
del xs[i]
Where i is the index. (e.g: Your song's index based on your search).
list.remove() is used for removing a matching element form the list not the "ith" item.
You might also find that a list is not a suitable data structure here? Perhaps you could try storing key/value pairs in a dict. e.g:
my_songs = {}
my_aongs["My Song Title"] = MySong(title, description, length)
You can later delete songs via their keys:
del my_songs["My Song Title"]
where titles are your keys. This saves you from doing O(n) searching.
Update:
Your .remove() method should look more like the following:
def remove(self, title):
for i, song in enumerate(self._mySongs):
if song.getTitle() == title:
del self._mySongs[i]
return
print("Song not found!")
Here we're using list's iteration protocol by using a for x in xs: rather than using a while loop and doing manual bookkeeping. The builtin function enumerate() is also used to give us an index into the list we're iterating over (i.e: it's position in the sequence).
try
self._mySongs.remove(title)
That should work.
(Or from another object: replace self by whatever your object name is)

Python: referencing class object list of lists

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.

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