Python List of classes - python

I'm trying to design a "Time Tracker" device. I want to be able to define a class line like:
class line():
def __init__(self, course, weekHours, hoursTotal, comment)
self.course = course
self.weekHours = weekHours
self.hoursTotal = hoursTotal
self.comment = comment
Then be able store an array (I guess it's called a list in Python?), of these class objects. So I can print a table produced by all of these lines and save these lines to an output file and then later be able to read that back into this list to view the table or make changes. do I declare table = [class line()]? If so, how do I access each of these objects in the list? I want to be able to differentiate them so I can edit a particular "line" if necessary.

You can store class instances in a list:
lines = []
lines.append(line('Math', '3', '12', 'Hello World!'))
...
To get the i'th line, you'd just do:
lines[i]
Note that there really isn't a good reason to have a class here. a python dict would be more efficient:
lines = []
lines.append({'course': 'Math', ...})

Related

Cleanest way to create instances of class dynamically

In caluclations.py I have one class called PowerPlant() with
def __init__(self, parameter1, parameter2, parameter3, ...)
and some functions belonging to the class e.g. calculate_supply(self, variable1, variable2).
I'd like to apply the calculations from calculations.py to some power plants stored in a csv file. So far i use the following way...
In simulation.py I read the data from the csv with pd.read_csv()
plant_data = pd.read_csv('plants.csv', sep = ';', index_col = False, encoding = 'latin')
Then i create a list of lists with
# Create list of lists from plant_data DataFrame
list_of_plants = [list(row) for row in plant_data.values]
Afterwards I create an instance of the Class PowerPlant with
## Outer loop: Iterate over all plants
for row in range(len(list_of_plants)):
ElectricityOut = []
Gains = []
...
# Initialise an instance of the plant
MyPowerPlant = PowerPlant(parameter1 = list_of_plants[row][0],
parameter2 = list_of_plants[row][1],
parameter3 = list_of_plants[row][2],
...)
# call some methods from calculations.py
...
Any ideas and suggetions how i could do this in a better and more professional way?
Maybe create a object for each plant?
You can iterate over a list like so, no need for range(len())
for row in list_of_plants:
ElectricityOut = []
Gains = []
...
# Initialise an instance of the plant
MyPowerPlant = PowerPlant(parameter1 = row[0],
parameter2 = row[0],
parameter3 = row[0],
...)
I'm not happy with accessing the list items with [item] e.g.
list_of_plants[row][0]
As far as i know there is no possibility to access lists via names (use dictionaries for that), but whats with namedTuples?
Is it possible to create instances of a class from namedTuples? If so i would change the list to a namedTuple...
Any suggestions?

Dynamically create instances of a class python

I'm new in python and I'm trying to dynamically create new instances in a class. So let me give you an example, if I have a class like this:
class Person(object):
def __init__(self, name, age, job):
self.name = name
self.age = age
self.job = job
As far as I know, for each new instance I have to insert, I would have to declare a variable and attach it to the person object, something like this:
variable = Person(name, age, job)
Is there a way in which I can dynamically do this? Lets suppose that I have a dictionary like this:
persons_database = {
'id' : ['name', age, 'job'], .....
}
Can I create a piece of code that can iterate over this db and automatically create new instances in the Person class?
Just iterate over the dictionary using a for loop.
people = []
for id in persons_database:
info = persons_database[id]
people.append(Person(info[0], info[1], info[2]))
Then the List people will have Person objects with the data from your persons_database dictionary
If you need to get the Person object from the original id you can use a dictionary to store the Person objects and can quickly find the correct Person.
people = {}
for id, data in persons_database.items():
people[id] = Person(data[0], data[1], data[2])
Then you can get the person you want from his/her id by doing people[id]. So to increment a person with id = 1's age you would do people[1].increment_age()
------ Slightly more advanced material below ----------------
Some people have mentioned using list/dictionary comprehensions to achieve what you want. Comprehensions would be slightly more efficient and more pythonic, but a little more difficult to understand if you are new to programming/python
As a dictionary comprehension the second piece of code would be people = {id: Person(*data) for id, data in persons_database.items()}
And just so nothing here goes unexplained... The * before a List in python unpacks the List as separate items in the sequential order of the list, so for a List l of length n, *l would evaluate to l[0], l[1], ... , l[n-2], l[n-1]
Sure, a simple list comprehension should do the trick:
people = [Person(*persons_database[pid]) for pid in persons_database]
This just loops through each key (id) in the person database and creates a person instance by passing through the list of attributes for that id directly as args to the Person() constructor.

How to add Objects to Class with a loop in Python?

I have a class which looks like this.
class CharInStageList(object):
def __init__(self, charid, charname) :
self.charid = charid
self.charname = charname
into this class I would like to add lists that I have.
I know how to do it the normal way
charOne = CharInStageList(1,'Tim')
charTwo = CharInStageList(2,'Struppi')
that's not a problem what I actually want to do is to add them by using a loop.
I get my data in this form
((1,'Tim'),(4,'Struppi'))
or
((1,'Tim'),(4,'Struppi'),(5,'Nami'),(6,'Luffy'))
the amount of characters I have in the scene is always different.
what I imagined would be a loop like this
charlist = ((1,'Tim'),(4,'Struppi'))
for char in charlist
objname = CharInStageList(char[0],char[1])
something like this
I want the objname to change by itself for every object I add to the class.
How can I get this effect?
I can only use python 2.6.6 for this since it's the maya 2013 python
Edit:
Thx #silas-ray #chepner #theodox I looked into Dicts a bit more and that's pretty much what I need
I use a modified version of #chepner method on it.
object_dict = dict( (y, CharInStageList(x,y)) for x,y in data )
Works like a charm
My testcode looks like this
import maya.cmds as cmds
dicttest = {}
def getdata ():
global dicttest
data = ((1,'Tim'),(4,'Struppi'),(5,'Nami'),(6,'Luffy'))
dicttest = dict( (y,(x,y)) for x,y in data )
getdata()
def printtest() :
for char in dicttest:
print dicttest[char]
printtest()
dicttest.clear()
I would have liked to comment in your answers with code examples but I can't get that to work there.
Objects are not added to a class. Instead, you can create a list of objects which are all instances of the same class, using a list comprehension and taking advantage of the *args syntax:
data = ((1,'Tim'),(4,'Struppi'),(5,'Nami'),(6,'Luffy'))
object_list = [ CharInStageList(*x) for x in data ]
Perhaps you want a dictionary instead:
object_dict = dict( (y, CharInStageList(x,y)) for x,y in data )
(Note that CharInStageList is a poor name for the class, because it's not a list; it encapsulates a single character.)
If you really want CharInStateList to be a collection of characters, try something like this, which is just a thin wrapper around a dictionary:
# Your former CharInStageList
class Character(object):
def __init__(self, charid, charname) :
self.charid = charid
self.charname = char name
class OnStageCharacters(object):
def __init__(self):
self.characters = dict()
# Index on-stage characters using their charid
def add(self, character):
self.characters[character.charid] = character
on_stage = OnStageCharacters()
for id, name in data:
on_stage.add( Character(id, name) )
You can't (at least not without hacking at locals/globals, which is generally not a good idea) change the name you are assigning to dynamically like that. You can, however, insert them in to a dictionary where the keys are your dynamically generated names.
characters = {}
for char_data in char_list:
characters[char_data[1]] = CharInStageList(*char_data)
Though if all your character objects are storing is name and id, it might make more sense to simplify the whole thing and just create mapping dictionaries rather than objects.
character_names_by_id = dict(char_data)
character_ids_by_name = dict((name, id) for id, name in char_data)
#chepner's answer is a great one if you can use the *args form to fill out your class instances.
If you're just asking the most efficient way to do this from a loop, remember you can have iterate over the parts of a tuple together:
data = ((1,'Tim'),(4,'Struppi'),(5,'Nami'),(6,'Luffy'))
class_data = (CharInStageList(id, name) for id, name in data) # "for id, name" will yield two vals
You can also use map, which is very common for doing bulk data translations. A common way to do it is with a lambda so you can write it clearly:
to_char = lambda k: CharInStageList(k[0], k[1])
class_data = map(to_char, data)
If you're doing something as simple as your example, you might not want to bother with your own class. the namedtuple is a great data structure for creating tuples that are easy to work with. It also means you can use positional or named args interchangeably, just as in #chepner's *args version:
StageListChar = namedtuple('CharInStageList', ['id', 'name'])
class_data = map(StageListChar , data)

Objects/classes/lists Python

I am confused about classes in python. I don't want anyone to write down raw code but suggest methods of doing it. Right now I have the following code...
def main():
lst = []
filename = 'yob' + input('Enter year: ') + '.txt'
for line in open(filename):
line = line.strip()
lst.append(line.split(',')
What this code does is have a input for a file based on a year. The program is placed in a folder with a bunch of text files that have different years to them. Then, I made a class...
class Names():
__slots__ = ('Name', 'Gender', 'Occurences')
This class just defines what objects I should make. The goal of the project is to build objects and create lists based off these objects. My main function returns a list containing several elements that look like the following:
[[jon, M, 190203], ...]
These elements have a name in lst[0], a gender M or F in [1] and a occurence in [3]. I'm trying to find the top 20 Male and Female candidates and print them out.
Goal-
There should be a function which creates a name entry, i.e. mkEntry. It should be
passed the appropriate information, build a new object, populate the fields, and return
it.
If all you want is a handy container class to hold your data in, I suggest using the namedtuple type factory from the collections module, which is designed for exactly this. You should probably also use the csv module to handle reading your file. Python comes with "batteries included", so learn to use the standard library!
from collections import namedtuple
import csv
Person = namedtuple('Person', ('name', 'gender', 'occurences')) # create our type
def main():
filename = 'yob' + input('Enter year: ') + '.txt'
with open(filename, newlines="") as f: # parameters differ a bit in Python 2
reader = csv.reader(f) # the reader handles splitting the lines for you
lst = [Person(*row) for row in reader]
Note: If you're using Python 2, the csv module needs you to open the file in binary mode (with a second argument of 'rb') rather than using the newlines parameter.
If your file had just the single person you used in your example output, you' get a list with one Person object:
>>> print(lst)
[Person(name='jon', gender='M', occurences=190203)]
You can access the various values either by index (like a list or tuple) or by attribute name (like a custom object):
>>> jon = lst[0]
>>> print(jon[0])
jon
>>> print(jon.gender)
M
In your class, add an __init__ method, like this:
def __init__(self, name, gender, occurrences):
self.Name = name
# etc.
Now you don't need a separate "make" method; just call the class itself as a constructor:
myname = Names(lst[0], etc.)
And that's all there is to it.
If you really want an mkEntry function anyway, it'll just be a one-liner: return Names(etc.)
I know you said not to write out the code but it's just easier to explain it this way. You don't need to use slots - they're for a specialised optimisation purpose (and if you don't know what it is, you don't need it).
class Person(object):
def __init__(self, name, gender, occurrences):
self.name = name
self.gender = gender
self.occurrences = occurrences
def main():
# read in the csv to create a list of Person objects
people = []
filename = 'yob' + input('Enter year: ') + '.txt'
for line in open(filename):
line = line.strip()
fields = line.split(',')
p = Person(fields[0], fields[1], int(fields[2]))
people.append(p)
# split into genders
p_m = [p for p in people if p.gender == 'M']
p_f = [p for p in people if p.gender == 'F']
# sort each by occurrences descending
p_m = sorted(p_m, key=lambda x: -x.occurrences)
p_f = sorted(p_f, key=lambda x: -x.occurrences)
# print out the first 20 of each
for p in p_m[:20]:
print p.name, p.gender, p.occurrences
for p in p_f[:20]:
print p.name, p.gender, p.occurrences
if __name__ == '__main__':
main()
I've used a couple of features here that might look a little scary, but they're easy enough once you get used to them (and you'll see them all over python code). List comprehensions give us an easy way of filtering our list of people into genders. lambda gives you an anonymous function. The [:20] syntax says, give me the first 20 elements of this list - refer to list slicing.
Your case is quite simple and you probably don't even really need the class / objects but it should give you an idea of how you use them. There's also a csv reading library in python that will help you out if the csvs are more complex (quoted fields etc).

Is there a way to find objects in a list without iterating through the list?

Is there some easy way to access an object in a list, without using an index or iterating through the list?
In brief:
I'm reading in lines from a text file, splitting up the lines, and creating objects from the info. I do not know what information will be in the text file. So for example:
roomsfile.txt
0\bedroom\A bedroom with king size bed.\A door to the east.
1\kitchen\A modern kitchen with steel and chrome.\A door to the west.
2\familyRoom\A huge family room with a tv and couch.\A door to the south.
Some Python Code:
class Rooms:
def __init__(self, roomNum, roomName, roomDesc, roomExits):
self.roomNum = roomNum
self.roomName = roomName
self.roomDesc = roomDesc
self.roomExits = roomExits
def getRoomNum(self):
return self.roomNum
def getRoomName(self):
return self.roomName
def getRoomDesc(self):
return self.roomDesc
def getRoomExits(self):
return self.roomExits
def roomSetup():
roomsfile = "roomsfile.txt"
infile = open(roomsfile, 'r')
rooms = []
for line in infile:
rooms.append(makeRooms(line))
infile.close()
return rooms
def makeRooms(infoStr):
roomNum, roomName, roomDesc, roomExits = infoStr.split("\")
return Rooms(roomNum, roomName, roomDesc, roomExits)
When I want to know what exits the bedroom has, I have to iterate through the list with something like the below (where "noun" is passed along by the user as "bedroom"):
def printRoomExits(rooms, noun):
numRooms = len(rooms)
for n in range(numRooms):
checkRoom = rooms[n].getRoomName()
if checkRoom == noun:
print(rooms[n].getRoomExits())
else:
pass
This works, but it feels like I am missing some easier approach...especially since I have a piece of the puzzle (ie, "bedroom" in this case)...and especially since the rooms list could have thousands of objects in it.
I could create an assignment:
bedroom = makeRooms(0, bedroom, etc, etc)
and then do:
bedroom.getRoomExits()
but again, I won't know what info will be in the text file, and don't know what assignments to make. This StackOverFlow answer argues against "dynamically created variables", and argues in favor of using a dictionary. I tried this approach, but I could not find a way to access the methods (and thus the info) of the named objects I added to the dictionary.
So in sum: am I missing something dumb?
Thanks in advance! And sorry for the book-length post - I wanted to give enough details.
chris
At least one dictionary is the right answer here. The way you want to set it up is at least to index by name:
def roomSetup():
roomsfile = "roomsfile.txt"
infile = open(roomsfile, 'r')
rooms = {}
for line in infile:
newroom = makeRooms(line)
rooms[newroom.roomName] = newroom
infile.close()
return rooms
Then, given a name, you can access the Rooms instance directly:
exits = rooms['bedroom'].roomExits
There is a reason I'm not using your getRoomName and getRoomExits methods - getter and setter methods are unnecessary in Python. You can just track your instance data directly, and if you later need to change the implementation refactor them into properties. It gives you all the flexibility of getters and setters without needing the boilerplate code up front.
Depending on what information is present in your definitions file and what your needs are, you can get fancier - for instance, I would probably want to have my exits information stored in a dictionary mapping a canonical name for each exit (probably starting with 'east', 'west', 'north' and 'south', and expanding to things like 'up', 'down' and 'dennis' as necessary) to a tuple of a longer description and the related Rooms instance.
I would also name the class Room rather than Rooms, but that's a style issue rather than important behavior.
You can use in to check for membership (literally, if something is in a container). This works for lists, strings, and other iterables.
>>> li = ['a','b','c']
>>> 'a' in li
True
>>> 'x' in li
False
After you've read your rooms, you can create a dictionary:
rooms = roomSetup()
exits_of_each_room = {}
for room in rooms:
exits_of_each_room[room.getRoomName()] = room.getRoomExits()
Then you your function is simply:
def printRoomExits(exits_of_each_room, noun):
print exits_of_each_room[noun]

Categories

Resources