I'm using Python and I have some data that I want to put into a tree format and assign codes to. Here's some example data:
Africa North Africa Algeria
Africa North Africa Morocco
Africa West Africa Ghana
Africa West Africa Sierra Leone
What would be an appropriate tree structure for this data?
Also, is there a way that I can then retrieve numerical codes from this tree structure, so that I could query the data and get codes like the following example?
def get_code(place_name):
# Python magic query to my tree structure
return code
get_code("Africa") # returns 1
get_code("North Africa") # returns 1.1
get_code("Morocco") # returns 1.1.2
Thank you for your help - I still have much to learn about Python :)
I would recommend, assuming you can count on there being no duplication among the names, something like:
class Node(object):
byname = {}
def __init__(self, name, parent=None):
self.name = name
self.parent = parent
self.children = []
self.byname[name] = self
if parent is None: # root pseudo-node
self.code = 0
else: # all normal nodes
self.parent.children.append(self)
self.code = len(self.parent.children)
def get_codes(self, codelist):
if self.code:
codelist.append(str(self.code))
self.parent.get_codes(codelist)
root = Node('')
def get_code(nodename):
node = Node.byname.get(nodename)
if node is None: return ''
codes = []
node.get_codes(codes)
codes.reverse()
return '.'.join(codes)
Do you also want to see the Python code for how to add a node given a hierarchical sequence of names, such as ['Africa', 'North Africa', 'Morocco']? I hope it would be pretty clear given the above structure, so you might want to do it yourself as an exercise, but of course do ask if you'd rather see a solution instead;-).
Getting the hierarchical sequence of names from a text line (string) depends on what the separators are -- in your example it looks like it's just a bunch of spaces added for purely aesthetic reasons connected with lining up the columns (if that's the case I'd recommend a simple re based approach to split on sequence of two+ spaces), but if it's actually (e.g.) tab characters as the separators, the csv module from Python's standard library would serve you better. I just can't tell from the short example you posted in your Q!-)
Edit: the OP says they can get the sequence of names just fine but would like to see the code to add the relevant nodes from those -- so, here goes!-)
def addnodes(names):
parent = root
for name in names:
newnode = Node.byname.get(name)
if newnode is None:
newnode = Node(name, parent)
parent = newnode
See why it's important that node names are unique, to make the above class work? Since Node.byname is a single per-class dict, it can record only one "corresponding node" for each given name -- thus, a name that's duplicated in two or more places in the hierarchy would "clash" and only one of the two or more nodes would be properly recorded.
But then again, the function get_code which the OP says is the main reason for this whole apparatus couldn't work as desired if a name could be ambiguous, since the OP's specs mandate it returning only one string. So, some geographical list like
America United States Georgia
Europe Eastern Europe Georgia
(where two completely unrelated areas just happen to be both named 'Georgia' -- just the kind of thing that unfortunately often happens in real-world geography, as the above example shows!-) would destroy the whole scheme (depending on how the specs for get_code happen to be altered to deal with an ambiguous-name argument, of course, the class structure could surely be altered accordingly and accomodate the new, drastically different specs!).
The nice thing about encapsulating these design decisions in a class (albeit in this case with a couple of accompanying functions -- they could be elegantly be made into class methods, of course, but the OP's specs rigidly demand that get_code be a function, so I decided that, in that case addnodes might as well also be one!-) is that the specific design decisions are mostly hidden from the rest of the code and thus can easily be altered (as long as specs never change, of course -- that's why it's so crucial to spend time and attention defining one's API specs, much more than on any other part of design and coding!-) to refactor the internal behavior (e.g. for optimization, ease of debugging/testing, and so on) while maintaining API-specified semantics intact, and thus leaving all other parts of the application pristine (not even needing re-testing, actually, as long of course as the parts that implement the API are very thoroughly unit-tested -- not hard to do, since they're nicely isolated and stand-alone!-).
An ad-hoc POD ("plain old data") class representing a tree would do fine, something like:
class Location(object):
def __init__(self, data, parent)
self.data = data
self.parent = parent
self.children = []
Now assign/read the data attribute, or add/remove children, perhaps with helper methods:
def add_child(self, child):
self.children.append(child)
Now, to actually divide your data into tree levels, a simple algorithm would be look at all the places with a common level-data (such as Africa) and assign them a location, then recursively for next level of data.
So, for Africa you create a location with data = Africa. Then, it will have a Location child for North Africa, West Africa and so on.
For "get the code" have a dictionary mapping each country to its location node, and use the parent links in the nodes. Traverse from the node to the top (until parent is None) at each level assigning the part of the code to be the index in the children list of the parent.
I am not sure, if I have got it right. If we keep every object in a global dict then it defeats the purpose of using a tree, which is only used to construct the numbering scheme.
But the tree based representation looks something like this:
class Location(object):
allLocation = {}
def __init__(self, name):
self.name = name
self.parent = None
self.number = "0"
self.children = {}
def putChild(self, childLocation):
if childLocation.name not in self.allLocation.keys():
# Now adjust the number scheme
#if self.number is "0":
# this is root
numScheme = str(len(self.children) + 1)
childLocation.setNumber(numScheme)
# Add the child
self.children[childLocation.number] = childLocation
self.allLocation[childLocation.name] = childLocation
childLocation.parent = self
return 0
else:
return 1 # Location already a child of the current clocation
def setNumber(self, num):
if self.number is not "0":
# raise an exception, number already adjusted
pass
else:
# set the number
self.number = num
def locateChild(self, numScheme):
# Logic should be to break up the numScheme and pass the token successively
numSchemeList = []
if type(numScheme) is str:
numSchemeList = numScheme.split(".")
else:
numSchemeList = numScheme
if len(numSchemeList) >= 1:
k = numSchemeList.pop()
# if the child is available
if k in self.children.keys():
childReferenced = self.children[k]
# Is child of child required
if len(numSchemeList) >= 1:
return childReferenced.locateChild(numSchemeList)
else:
return childReferenced
else:
# No such child
return None
else:
# The list is empty , search ends here
return None
def getScheme(self, name):
if name in self.allLocation.keys():
locObj = self.allLocation[name]
return locObj.getNumScheme(name, "")
else:
return None
def getNumScheme(self, name, numScheme="0",):
if not self.parent:
return numScheme
if numScheme != "":
return self.parent.getNumScheme(name, self.number + "." + numScheme)
else:
return self.parent.getNumScheme(name, self.number )
root = Location("root")
africa = Location("Africa")
asia = Location("Asia")
america = Location("America")
root.putChild(africa)
root.putChild(asia)
root.putChild(america)
nafrica = Location("North Africa")
africa.putChild(nafrica)
nafrica.putChild(Location("Morrocco"))
obj = root.locateChild("1.1.1")
print obj.name
print root.getScheme("Morrocco")
This code can be hideous. But, I just want to paste it because I have put some time into it :)
tree = file_to_list_of_tuples(thefile)
d = {}
i = 1
for continent, region, country in tree:
if continent not in d:
d[continent] = [i, 0, 0]
i += 1
cont_code = d[continent][0]
if region not in d:
max_reg_code = max( [y for x, y, z in d.values() if x==cont_code] )
d[region] = [cont_code, max_reg_code+1 , 0]
reg_code = d[region][1]
if country not in d:
max_country_code = max( [z for x, y, z in d.values() if x == cont_code and y== reg_code] )
d[country] = [cont_code, reg_code, max_country_code+1]
def get_code(x):
print d[x]
get_code will print lists, but you can easily make them print in the format you want.
You might use itertree package (I'm the author):
from itertree import *
#1. create the tree:
root2=iTree('root')
root2.append(iTree('Africa'))
root2[0].append(iTree('North Africa'))
root2[0].append(iTree('West Africa'))
root2[0][0].append(iTree('Algeria'))
root2[0][0].append(iTree('Morocco'))
item=iTree('Ghana') # keep the item for easier access
root2[0][1].append(item)
root2[0][1].append(iTree('Sierra Leone'))
# get the index path information of an item:
print('"Ghana" item index path:',item.idx_path)
# you can also search for items:
result = root2.find(['**', 'Morroco'])
print('"Morroco" item index path:', result.idx_path)
executing the script will deliver:
"Ghana" item index path: [0, 1, 0]
"Morroco" item index path [0, 0, 1]
Beside this you might add additional data to each item and do filtered searches.
Related
I solved an exercise where I had to apply a recursive algorithm to a tree that's so defined:
class GenericTree:
""" A tree in which each node can have any number of children.
Each node is linked to its parent and to its immediate sibling on the right
"""
def __init__(self, data):
self._data = data
self._child = None
self._sibling = None
self._parent = None
I had to concatenate the data of the leaves with the data of the parents and so on until we arrive to the root that will have the sum of all the leaves data. I solved it in this way and it works but it seems very tortuous and mechanic:
def marvelous(self):
""" MODIFIES each node data replacing it with the concatenation
of its leaves data
- MUST USE a recursive solution
- assume node data is always a string
"""
if not self._child: #If there isn't any child
self._data=self._data #the value remains the same
if self._child: #If there are children
if self._child._child: #if there are niece
self._child.marvelous() #reapply the function to them
else: #if not nieces
self._data=self._child._data #initializing the name of our root node with the name of its 1st son
#if there are other sons, we'll add them to the root name
if self._child._sibling: #check
current=self._child._sibling #iterating through the sons-siblings line
while current:
current.marvelous() #we reapplying the function to them to replacing them with their concatenation (bottom-up process)
self._data+=current._data #we sum the sibling content to the node data
current=current._sibling #next for the iteration
#To add the new names to the new root node name:
self._data="" #initializing the root str value
current=self._child #having the child that through recursion have the correct str values, i can sum all them to the root node
while current:
self._data+=current._data
current=current._sibling
if self._sibling: #if there are siblings, they need to go through the function themselves
self._sibling.marvelous()
Basically I check if the node tree passed has children: if not, it remains with the same data.
If there are children, I check if there are nieces: in this case I restart the algorithm until I can some the leaves to the pre-terminal nodes, and I sum the leaves values to put that sum to their parents'data.
Then, I act on the root node with the code after the first while loop, so to put its name as the sum of all the leaves.
The final piece of code serves as to make the code ok for the siblings in each step.
How can I improve it?
It seems to me that your method performs a lot of redundant recursive calls.
For example this loop in your code:
while current:
current.marvelous()
self._data += current._data
current = current._sibling
is useless because the recursive call will be anyway performed by the last
instruction in your method (self._sibling.marvelous()). Besides,
you update self._data and then right after the loop you reset
self._data to "".
I tried to simplify it and came up with this solution that seems to
work.
def marvelous(self):
if self.child:
self.child.marvelous()
# at that point we know that the data for all the tree
# rooted in self have been computed. we collect these
self.data = ""
current = self.child
while current:
self.data += current.data
current = current.sibling
if self.sibling:
self.sibling.marvelous()
And here is a simpler solution:
def marvelous2(self):
if not self.child:
result = self.data
else:
result = self.child.marvelous2()
self.data = result
if self.sibling:
result += self.sibling.marvelous2()
return result
marvelous2 returns the data computed for a node and all its siblings. This avoids performing the while loop of the previous solution.
I am new to Python and currently searching for some internship or a job. I am currently working on a program in Python which reads a file that contains data in this shape:
Id;name;surname;age;gender;friends;
Id and age are the positive integers,
gender can be "male" or "female",
and friends is an array of numbers, separated by comma, which represent the Id's of persons who are friends with the current person. If Person1 is a friend to a Person2, it must work vice versa.
As you can see in the above example, attributes of a "Person" are separated by semicolon, and the trick is that not every person has every attribute, and of course, they differ by the number of friends. So, the first part of the task is to make a program which reads a file and creates a structure which represents a list of persons with the attributs mentioned above. I have to make a search for those persons by Id.
The second part is to make a function with two arguments (Id1, Id2) which returns True if a person with Id2 is a friend to a person with Id1. Otherwise, it returns false.
I have some ideas on my mind, but I am not sure how to realize this, since I don't know enough about Python yet. I guess the best structure for this would be a dictionary, but I am not sure how to load a file into it, since the attributes of all persons are different. I would be greatful for any help you can offer me.
Here is my attempt to write the code:
people = open(r"data.txt")
class People:
id = None
name = ''
surname = ''
age = None
gender = ['male', 'female']
friends = []
#def people(self):
# person = {'id': None,
# 'name': '',
# 'surname': '',
# 'age': None,
# 'gender': ['male', 'female'],
# 'friends': []
# }
# return person
def community(self):
comm = [People()]
return comm
def is_friend(id1, id2):
if (id1 in People.friends) & (id2 in People.friends):
return True
people.close()
Your question is too broad imho, but I'll give you a few hints:
the simplest datastructure for O(n) key access is indeed a dict. Note that a dict needs immutable values as keys (but that's fine since your Ids are integers), but can take anything as values. but that only works for (relatively) small datasets since it's all in memory. If you need bigger datasets and/or persistance, you want a database (key:value, relational, document, the choice is up to you).
Python has classes and computed attributes
In Python, the absence of a value is the None object
there's a csv files parser in the standard lib.
Now you just have to read the doc and start coding.
[edit] wrt/ your code snippet
class People:
id = None
name = ''
surname = ''
age = None
gender = ['male', 'female']
friends = []
Python is not Java or PHP. What you defined above are class attributes (shared by all instances of the class), you want instance attributes (defined in the __init() method). You should really read the FineManual.
Also if you're using Python 2.7.x, you want your classes to inherit from object (historical reasons).
So your Person class should look something like this:
class Person(object):
def __init__(self, id, name, surname, age, gender, friends=None):
self.id = id
self.name = name
self.surname = surname
self.age = age
self.gender = gender
self.friends = friends or []
And then to create a Person instance:
person = Person(42, "John Cleese", "Archie Leach", 77, "male", [11, 1337)])
def is_friend(id1, id2):
if (id1 in People.friends) & (id2 in People.friends):
return True
A few points points here:
First: you either want to rename this function are_friends or make it a method of the Person class and then only pass a (single) Person instance (not an 'id') as argument.
Second: in Python, & is the bitwise operator. The logical "and" operator is spelled, well, and.
Third: an expression has a truth value by itself, so your if statement is redundant. Whenever you see something like:
def func():
if <some expression>:
return True
else:
return False
you can just rewrite it as :
def func():
return <some expression>
Or if you want to ensure func returns a proper boolean (True or False):
def func():
return bool(<some expression>)
I'll stop here because I don't intend to teach you how to program. You obviously need to do at least the full official Python tutorial, and possibly some complete beginner tutorial too.
I created a class that is formatted as follows:
class PathStructure(object):
def __init__(self, Description, ID, Parent):
self.Description = Description
self.ID = ID
self.Parent = Parent
self.Children = []
Where Description, ID and Parent are strings, and Children are lists of PathStructure objects; thus, I know all the parent-child relationships. I want to be able to construct a graphical representation of this tree, so each PathStructure object becomes a node with the parent-child relationships linking the nodes. Creating the nodes is easy, I think:
nodes = {}
For item in pathstructure_list:
name = item.Description
nodes[name] = item
I am having trouble thinking of a way to link these nodes into create a tree structure out of the linked nodes. I have looked at examples, but I am kind of new to using dicts, so I don't really understand the solutions -- especially since I will be constructing a dict of objects.
EDIT:
To clarify, I initialize each PathStructure object from a spreadsheet of information, and then I determine the parent-child relationships. For example:
first = PathStructure('Master','1-234-5',None)
second = PathStructure('Sub One','2-345-6',first.ID)
third = PathStructure('Sub Two','3-456-7',first.ID)
fourth = PathStructure('Sub Three','4-597-8',second.ID)
pathstructs = [first, second, third, fourth]
And then I determine each object's children through a function, so I know each object's parent and child.
I was able to get close to where I want to be with the following:
full = collections.defaultdict(dict)
for item in pathstructs:
name = item.Description
ident = item.ID
perent = item.Parent
final = full[ident]
if parent:
full[parent][ident] = final
else:
root = final
But this method gets rid of the PathStructure objects, so I am stuck with a tree of string instead of object.
I have a number of chemicals with corresponding data held within a database, how do I go about returning a specific chemical, and its data, via its formula, eg o2.
class SourceNotDefinedException(Exception):
def __init__(self, message):
super(SourceNotDefinedException, self).__init__(message)
class tvorechoObject(object):
"""The class stores a pair of objects, "tv" objects, and "echo" objects. They are accessed
simply by doing .tv, or .echo. If it does not exist, it will fall back to the other variable.
If neither are present, it returns None."""
def __init__(self, echo=None, tv=None):
self.tv = tv
self.echo = echo
def __repr__(self):
return str({"echo": self.echo, "tv": self.tv}) # Returns the respective strings
def __getattribute__(self, item):
"""Altered __getattribute__() function to return the alternative of .echo / .tv if the requested
attribute is None."""
if item in ["echo", "tv"]:
if object.__getattribute__(self,"echo") is None: # Echo data not present
return object.__getattribute__(self,"tv") # Select TV data
elif object.__getattribute__(self,"tv") is None: # TV data not present
return object.__getattribute__(self,"echo") # Select Echo data
else:
return object.__getattribute__(self,item) # Return all data
else:
return object.__getattribute__(self,item) # Return all data
class Chemical(object):
def __init__(self, inputLine, sourceType=None):
self.chemicalName = TVorEchoObject()
self.mass = TVorEchoObject()
self.charge = TVorEchoObject()
self.readIn(inputLine, sourceType=sourceType)
def readIn(self, inputLine, sourceType=None):
if sourceType.lower() == "echo": # Parsed chemical line for Echo format
chemicalName = inputLine.split(":")[0].strip()
mass = inputLine.split(":")[1].split(";")[0].strip()
charge = inputLine.split(";")[1].split("]")[0].strip()
# Store the objects
self.chemicalName.echo = chemicalName
self.mass.echo = mass
self.charge.echo = charge
elif sourceType.lower() == "tv": # Parsed chemical line for TV format
chemicalName = inputLine.split(":")[0].strip()
charge = inputLine.split(":")[1].split(";")[0].strip()
mass = inputLine.split(";")[1].split("&")[0].strip()
# Store the objects
self.chemicalName.tv = chemicalName
self.charge.tv = charge
self.mass.tv = molecularWeight
else:
raise SourceNotDefinedException(sourceType + " is not a valid `sourceType`") # Otherwise print
def toDict(self, priority="echo"):
"""Returns a dictionary of all the variables, in the form {"mass":<>, "charge":<>, ...}.
Design used is to be passed into the Echo and TV style line format statements."""
if priority in ["echo", "tv"]:
# Creating the dictionary by a large, to avoid repeated text
return dict([(attributeName, self.__getattribute__(attributeName).__getattribute__(priority))
for attributeName in ["chemicalName", "mass", "charge"]])
else:
raise SourceNotDefinedException("{0} source type not recognised.".format(priority)) # Otherwise print
from ParseClasses import Chemical
allChemical = []
chemicalFiles = ("/home/temp.txt")
for fileName in chemicalFiles:
with open(fileName) as sourceFile:
for line in sourceFile:
allChemical.append(Chemical(line, sourceType=sourceType))
for chemical in allChemical:
print chemical.chemicalName #Prints all chemicals and their data in list format
for chemical in allChemical(["o2"]):
print chemical.chemicalName
outputs the following error which I have tried to remedy with no luck;
TypeError: 'list' object is not callable
The issue is the two lines
for chemical in allChemical(["o2"]):
print chemical.chemicalName
allChemical is a list, and you can't just do a_list(). It looks like you're trying to find either ['o2'] or just 'o2' in a list. To do that, you can get the index of the item and then get that index from the list.
allChemical[allChemical.index("o2")]
Try this function:
def chemByString(chemName,chemicals,priority="echo"):
for chemical in chemicals:
chemDict = chemical.toDict(priority)
if chemDict["chemicalName"] == chemName
return chemical
return None
This function is using the toDict() method found in the Chemical class. The code you pasted from the Chemical class explains that this method returns a dictionary from the chemical object:
def toDict(self, priority="echo"):
"""Returns a dictionary of all the variables, in the form {"mass":<>, "charge":<>, ...}.
Design used is to be passed into the Echo and TV style line format statements."""
if priority in ["echo", "tv"]:
# Creating the dictionary by a large, to avoid repeated text
return dict([(attributeName, self.__getattribute__(attributeName).__getattribute__(priority))
for attributeName in ["chemicalName", "mass", "charge"]])
else:
raise SourceNotDefinedException("{0} source type not recognised.".format(priority)) # Otherwise print
This dictionary looks like this:
"chemicalName" : <the chemical name>
"mass" : <the mass>
"charge" : <the charge>
What the function I created above does is iterate through all of the chemicals in the list, finds the first one with a name equal to "o2", and returns that chemical. Here's how to use it:
chemByString("o2",allChemicals).chemicalName
If the above does not work, may want to try using the alternative priority ("tv"), though I'm unsure if this will have any effect:
chemByString("o2",allChemicals,"tv").chemicalName
If the chemical isn't found, the function returns None:
chemByString("myPretendChemical",allChemicals).chemicalName
EDIT: See my new answer. Leaving this one here since it might still be helpful info.
In python, a list object is a structure holding other objects with an index for each object it contains. Like this:
Index Object
0 "hello"
1 "world"
2 "spam"
If you want to get to one of those objects, you have to know its index:
objList[0] #returns "hello" string object
If you don't know the index, you can find it using the index method:
objList.index("hello") #returns 0
Then you can get the object out of the list using the found index:
objList[objList.index("hello")]
However this is kind of silly, since you can just do:
"hello"
Which in this case will produce the same result.
Your allChemical object is a list. It looks like the line chemicalFiles = ("/home/temp.txt") is filling your list with some type of object. In order to answer your question, you have to provide more information about the objects which the list contains. I assume that information is in the ParseClasses module you are using.
If you can provide more information about the Chemical object you are importing, that may go a long way to helping solve your problem.
IF the objects contained in your list are subclassed from str, this MAY work:
allChemical[allChemical.index("o2")].chemicalName
"02" is a str object, so index is going to look for a str object (or an object subclassed from str) in your list to find its index. However, if the object isn't a string, it will not find it.
As a learning exercise, try this:
class Chemical(str):
'''A class which is a subclass of string but has additional attributes such as chemicalName'''
def __init__(self,chemicalName):
self.chemicalName = chemicalName
someChemicals = [Chemical('o2'),Chemical('n2'),Chemical('h2')]
for chemical in someChemicals: print(chemical.chemicalName)
#prints all the chemical names
print(someChemicals[0].chemicalName)
#prints "o2"; notice you have to know the index ahead of time
print(someChemicals[someChemicals.index("o2")].chemicalName)
#prints "o2" again; this time index found it for you, but
#you already knew the object ahead of time anyway, sot it's a little silly
This works because index is able to find what you are looking for. If it isn't a string it can't find it, and if you don't know what index 'o2' is at, if you want to get to a specific chemical in your list of chemicals you're going to have to learn more about those objects.
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]