Hello Friends I'm a newbie to Python and trying to implement some sample code using Classes in Python. I could implement using individual functions but when I'm trying to integrate via class I'm ending up in infinite loop.
Here is the snippet of code and logic I'm reading a file where contents are like as follows:
File input.txt contents
fruit apple
animal cat
vehicle car
My final aim is to get output as dictionary of contents with key and value like below dict and later I want to search with keys and do processing.
{'fruit': 'apple', 'animal': 'cat', 'vehicle':'car'}
class FileHandler:
def __init__(self, dbf):
self.logger = logging.getLogger('fileHandler')
self.thefile = open(dbf, 'r')
print(self.thefile)
def __enter__(self):
return self
def __exit__(self,type,value,traceback):
self.thefile.close()
with FileHandler(dbf='input.txt') as fh:
config = [line.strip() for line in (fh.thefile.readlines())]
Here is the class definition:
class GetExtract:
def __init__(self, config_list=None, config_dict=None):
if (config_list, config_dict) is None:
config_list = []
config_dict = {}
self.config_list = config_list
self.config_dict = config_dict
def assign_sig(self, listin):
self.config_list = listin
#for item in listin:
# self.config_list = listin.extend(item.split())
#print("final dict is \n", self.config_list) ## break items into list
## Map adjacent items as key and value
#for index, kvalue in enumerate(self.config_list):
# if index %2 == 0:
# self.config_dict[kvalue] = self.config_list[index+1]
# return self.config_dict ## create a mapping inside dict
xtemp = GetExtract()
xtemp.assign_sig(config)
When I try to iterate using the for loop inside the class it goes into an infinite loop which I have commented in the above code for.
Please advise me how to achieve my aim using classes.
for item in listin:
self.config_list = listin.extend(item.split())
The object you are looping over (in this case listin) is being modified whithin the loop. Never do that.
Related
I am trying to import this list item from the main function:
character1 = Character("Conan the Barbarian")
for test_item in ["sword", "sausage", "plate armor", "sausage", "sausage"]:
character1.give_item(test_item)
I am using class and in the class there is those methods to save the list to the character
class Character:
def __init__(self, character):
self.__character = character
self.__dct = {}
def give_item(self, items):
self.__dct[self.__character] = items
def printout(self):
for characters in self.__dct:
print(f'Name:', characters)
for items in self.__dct.keys():
print(self.__dct[self.__character])
my output is printing only the last entry from the list, seems like the entries being overwritten. But I can't really figure why.
my output
Name: Conan the Barbarian
sausage
I want my output to be:
Name: Conan the Barbarian
plate armor
sausage
sword
This is because you're reusing your key. Every time, you assign to self.__dict[self.__character]
Your loop for items in self.__dict.keys()) is oddly written. items here is actually a list of keys, and will therefore only run once, when in actuality you want to run it # items times.
Instead, you could use a list (or set):
def __init__(self, character):
self.__character = character
self.__dct = {}
self.__dct[self.__character] = []
def printout(self):
for characters in self.__dct:
print(f'Name:', characters)
for item in self.__dct[self.__character]:
print(item)
It seems like your self.__dct[self.__character] = items line uses the [self.__character] as the key to append to your dict. In a Python Dictionnary, you can just add your list as easy as writing the key and putting the list as the value like so:
self.__dct = {
"name" = character_name,
"items" = list_of_items
}
I am trying to create a program where I can store NPCs in a game with certain attributes. Such as: Faction, personality, interests/hobbies. To achieve this, I have created an NPC class.
class NPC: #name, faction, position/job, character, interests, other
def __init__ (self, name, faction, pos, char, inter, misc):
self.name = name
self.faction = faction
self.pos = pos
self.char = char
self.inter = inter
self.misc = misc
I have created various functions for this program, such as creating new ones, changing certain attributes on NPCs, removing them, printing them and sorting them. To store the NPCs, I append them to a list named "NPClist". I would like to know how to save this list to a .text file or something. So far I have tried the pickle module but that doesn't seem to work. (Sourced from: How to save a list to a file and read it as a list type?)
with open("NPCs.text", "wb") as file:
pickle.dump(NPClist, file)
with open("NPCs.text", "rb") as file:
NPClist.append(pickle.load(file))
I have put the bottom one at the top of the program so that it will load it when the program is launched and the top one at the top of a loop so that it will save frequently. When I try starting the program I get an error message.
AttributeError: Can't get attribute 'NPC' on <module '__main__' (built-in)>
Is there another way to solve this problem or am I just doing pickle the wrong way?
If all you need are the attributes I would suggest just saving the attributes instead of trying to save the entire object, and make this process easier with some helper methods in NPC.
For instance:
class NPC:
def dump(self):
return [self.name, self.faction, self.pos, self.char, self.inter, self.misc]
#staticmethod
def build_npc(attributes):
return NPC(*attributes)
And then you may deal with dumping like so:
NPClist = [NPC(...), NPC(...) ... ]
with open("NPCs.text", "wb") as file:
pickle.dump([i.dump() for i in NPClist], file)
And loading like so:
with open("NPCs.text", "rb") as file:
NPClist = [NPC.build_npc(attributes) for attributes in pickle.load(file)]
class NPC: #name, faction, position/job, character, interests, other
def __init__ (self, name, faction, pos, char, inter, misc):
self.name = name
self.faction = faction
self.pos = pos
self.char = char
self.inter = inter
self.misc = misc
NPCList = []
handsome_npc = NPC(name='n1c9', faction='Good People', pos='Developer',
char='', inter='', misc='')
# create other NPCs as needed
NPCList.append(handsome_npc)
with open('NPCS.text', 'w') as f:
f.write('name,faction,pos\n')
# add other attrs as wanted
for npc in NPCList:
f.write(f"{npc.name}, {npc.faction}, {npc.pos}")
# add other attrs as wanted
f.write('\n')
Tried to write something that's accessible to a beginner - might be a little verbose because of that. Mark Tyler's answer is really good, too!
re: comment - you could access the file afterwards like so:
class NPC: #name, faction, position/job, character, interests, other
def __init__ (self, name, faction, pos, char, inter, misc):
self.name = name
self.faction = faction
self.pos = pos
self.char = char
self.inter = inter
self.misc = misc
npclist_built_from_file = []
with open('NPCS.text', 'r') as f:
NPCS_lines = f.readlines()
for line in NPCS_lines[1:]: # skip the header line
npc = NPC(name=line[0], faction=line[1], pos=line[2], char='', inter='', misc='')
# I used empty strings for char/inter/misc because they were empty in the original
# example, but you would just fill out line[3], line[4], line[5] for the rest if wanted.
npclist_built_from_file.append(npc)
Then you could do whatever you wanted with the NPC objects in the list npclist_built_from_file
import ast
def stringifyNPC(c):
return str(c.__dict__)
def unStringifyNPC(s):
n = NPC(None,None,None,None,None,None)
n.__dict__ = ast.literal_eval(s)
return n
I have:
if you wanted to accomplish this with classes instead of functions so you could import a csv and run it on new data.
Which class would you make first and how would you iterate through the class to compare each piece of data as a part is in every building with different quantities but the mass of that data is in a file
Appreciate your help.. sorry for vagueness just want to see if there are any suggestions!
part = [1,2,3,4,5]
building = [1,2,3,4,5]
qty = [1,2,3,4,5]
You would just make the dictionary a data member of the class
class Container:
def __init__(self):
self.data = {"part": [], # Data member of class
"building": [],
"qty": []}
# Pass self to method of class, so it can access data members
def ingest_csv(self, filepath):
with open(filepath, "r") as file:
for line in file[1:]:
part, building, qty = line.split(",")
self.data["part"].append(part)
self.data["building"].append(building)
self.data["qty"].append(qty)
edit: then to use it you would do the following.
container = Container()
container.ingest_csv("./path/to.csv")
# Print the dict if you want to view it
print(container.data)
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']
I want to use a list throughout a program I am writing. Basically, it is a list full of tuples with information regarding different people, each person's information (name, phone, address, etc) is stored as in a tuple. I define this list through an initial function, but i need to use this in my interaction function as well as others.
My question is, is it possible for me to use this list without defining it as a global variable?
def load_friends(filename):
"""imports filename as a list of tuples using the import command"""
import csv
with open(filename, 'Ur')as filename:
friends_list = list(tuple(x) for x in csv.reader(filename, delimiter=','))
def add_friend(friend_info, friends_list):
"""appends the friend_info tupple to the list friends_list"""
new_list = friends_list.append(friends_info)
def interact():
"""interaction function: accepts user input commands"""
while True:
command = raw_input('Command: ')
I should also mention that there is a command to parse the use inputs to perform the functions. Would this affect the use of the list?
You could declare list inside the first function that calls it and return it from there, latter functions should receive this list as an argument then.
def func1():
my_list=[]
"""Do stuff
"""
return list
def func2(my_list):
"""Do stuff with my_list
"""
return
def func3(my_list):
"""Do stuff with my_list
"""
return
def main():
"""First we retrieve the list from func1,
func2/3 get it passed to them as an argument
"""
foo=func1
func2(foo)
func3(foo)
if __name__ == "__main__":
main()
You could do the following:
# you can define the list here, it's global but doesn't require the keyword
my_list_globally = []
def func1(the_list):
pass
def func2(the_list):
pass
def func3(the_list):
pass
# you can use a hub function to pass the list into things that need it
def main():
my_list = []
func1(my_list)
func2(my_list)
func3(my_list)
if __name__ == "__main__":
main()
I don't quite understand the last part of your question but one of those 2 ways will be what you need.
Yes. Pass the "list of friends" back and forth between functions as an argument.
load_friends() would become
def load_friends(filename):
import csv
with open(filename, 'Ur') as f:
return map(tuple, csv.reader(f, delimiter=","))
add_friend() is close, but that assignment to new_list is unnecessary, because list.append() mutates the existing list in place:
def add_friend(friend_info, friend_list):
friend_list.append(friend_info)
would suffice.
interact() would also have a friends_list argument.
def interact(friends_list):
#interaction stuff here...
and you could call it like so:
interact(load_friends("myfile.csv"))
Classes are useful for this kind of thing, and easy to use:
class PersInfo:
def setName(self, name):
self._name = name
def getName(self):
return self._name
def setNumber(self, number):
self._phNumber = number
def getNumber(self):
return self._phNumber
def setAddr(self, address):
self._address = address
def getAddr(self)
return self._address
def main():
# Read in data from your CSV Here
infoList = ([])
for person in person_list: # Assuming person is a tuple here
foo = PersInfo()
foo.setName(person[index1])
foo.setNumber(person[index2])
foo.setAddr(person[index3])
infoList.append(foo)
# To access the info...
for person in infoList:
print(person.getName())
print(person.getNumber())
print(person.getAddr())
You do end up with the list being "global," sort of. It is in the main() function, where the PersInfo objects are being instantiated. This may be more than you wanted, but in the long run it is a good way to organize your code and keep it readable.
Also, you could build the infoList I made directly where you are creating person_list.