using a dictionary after it's been unpickled (in Python)

Discussion in 'Programming' started by Noodle, Jul 26, 2006.

  1. #1
    This is actually a homework assingment, so I'm not looking for a direct answer, but maybe a point in the right direction. The assingment is make this program not "forget" all the information the user entered everytime it ends. (using pickle). I added the nodePickle() and nodeUnpickle() and a few other things (I noted the things I added in the comments, the rest is the orginal program)

    The problem I have, is that when I try to send the var ('item') holding the dictionary to the Qnode class (after I've loaded it from the file) , I get this error: "cannot concatenate 'str' and 'instance' objects." So, I'm not really sure what is going on here. The lesson plan really doesn't go into detail about this, so I'm kinda lost.
    
    # questor.py		
    import cPickle as p
    
    # define some constants for future use
    
    kQuestion = 'question'
    kGuess = 'guess'
    nodeFile = 'questFile.txt' # jason added
    
    
    # define a function for asking yes/no questions
    def yesno(prompt):
    	ans = raw_input(prompt)
    	return (ans[0]=='y' or ans[0]=='Y')
    
    # I added; dump dictionary to file for future use
    def nodePickle(nodes):
            f = file(nodeFile, 'w')
            p.dump(nodes, f)
            f.close
            f = file(nodeFile)
            theNode = p.load(f)
            print(theNode)
    
    # I added; load dictionary from file
    def nodeUnpickle():
            f = file(nodeFile)
            nodes = p.load(f)
            f.close
            return nodes
    
    # define a node in the question tree (either question or guess)
    class Qnode:
    	
    	# initialization method
    	def __init__(self,guess):
    		self.nodetype = kGuess
    		self.desc = guess
    
    	# get the question to ask	
    	def query(self):
    		if (self.nodetype == kQuestion):
    			return self.desc + " "
    		elif (self.nodetype == kGuess):
    			return "Is it a " + self.desc + "? "
    		else:
    			return "Error: invalid node type!"
    
    	# return new node, given a boolean response
    	def nextnode(self,answer):
    		return self.nodes[answer]
    
    	# turn a guess node into a question node and add new item
    	# give a question, the new item, and the answer for that item
    	def makeQuest( self, question, newitem, newanswer ):
    
    		# create new nodes for the new answer and old answer
    		newAnsNode = Qnode(newitem)
    		oldAnsNode = Qnode(self.desc)
                    print ("newAns: ", newAnsNode)
                    print ("oldAns: ", oldAnsNode)
                    
    		# turn this node into a question node
    		self.nodetype = kQuestion
    		self.desc = question
    
    		# assign the yes and no nodes appropriately
    		self.nodes = {newanswer:newAnsNode, not newanswer:oldAnsNode}
                    nodePickle(self.nodes) # I added
                  
    
    def traverse(fromNode):
    	# ask the question
    	yes = yesno( fromNode.query() )
    	
    	# if this is a guess node, then did we get it right?
    	if (fromNode.nodetype == kGuess):
    		if (yes):
    			print "I'm a genius!!!"
    			return
    		# if we didn't get it right, return the node
    		return fromNode
    	
    	# if it's a question node, then ask another question
    	return traverse( fromNode.nextnode(yes) )
    
    def run(item):   
            # start with a single guess node
    	topNode = Qnode(item[0]) # orignal program read 'topNode = Qnode('python')
    	
    	done = 0
    	while not done:
    		# ask questions till we get to the end
    		result = traverse( topNode )
    		
    		# if result is a node, we need to add a question
    		if (result):
    			item = raw_input("OK, what were you thinking of? ")
    			print "Enter a question that distinguishes a",
    			print item, "from a", result.desc + ":"
    			q = raw_input()
    			ans = yesno("What is the answer for " + item + "? ")
    			result.makeQuest( q, item, ans )
    			print "Got it."
    		
    		# repeat until done
    		print
    		done = not yesno("Do another? ")
    		print
    
    
    # immediate-mode commands, for drag-and-drop or execfile() execution
    if __name__ == '__main__':
            item = nodeUnpickle()# I added; get dictionary from file
    	run(item)
    	print
    	raw_input("press Return>")
    else:
    	print "Module questor imported."
    	print "To run, type: questor.run()"
    	print "To reload after changes to the source, type: reload(questor)"
    
    # end of questor.py
    Code (markup):

    Also, as a side question.... how come when I load the dictionary into a var and try to "print" it... it gives me the memory address instead of the dictionary. I.E....

    >>> item = p.load(f)
    >>> item
    {False: <__main__.Qnode instance at 0x016E8080>, True: <__main__.Qnode instance at 0x016E8058>}

    or

    >>> item[0]
    <__main__.Qnode instance at 0x016E8080>

    Any help on these issue are appreciated, thanks in advance!

    -Noodle
     
    Noodle, Jul 26, 2006 IP
  2. jimrthy

    jimrthy Guest

    Messages:
    283
    Likes Received:
    13
    Best Answers:
    0
    Trophy Points:
    0
    #2
    I'm a little unclear about what you mean here. When do you actually do this, and where does the error happen? (Yeah, I could just load it up and see for myself, but you did say not to give you the full answer).

    I get this kind of error in python all the time :) (see my sig). It's a very forgiving language, and the dynamic typing is very nice. But sometimes it can bite you. Like now, when you think you have a couple of strings you want to concatenate, but one of those things isn't really a string.

    Or maybe you didn't even realize you were trying to concatenate a couple of strings. Look at the details of the error you're getting [exception stack traces are your friend]. You've probably already done that though, so maybe you should post that and we can help from that angle.

    BTW, I'm not sure, but it looks to me as if the code won't do what you want, even without this error.

    Personally, I recommend a different approach. I'd probably build a list of QNodes in the loop. Wrap the loop in a try/finally block (if you haven't covered exceptions yet, ignore that part), and then just pickle the list at the end [or in the finally block].

    It has to do with the way python converts objects to strings. In the first case, it is printing the dictionary. That's what the {} mean. False: and True: are the two keys. the <__main__...> bits are its attempt to print the values attached to those two keys.

    The second example narrows it down to just one of the values. Python doesn't know how to print a Qnode. So it has that default value. __main__ is the name of the module, Qnode is the class name. Then it shows a memory value to help you distinguish from other Qnode's.

    It's ugly, but it does the job (sort of).

    A debugging technique I love is to change this. There's usually much more useful information that you could be looking at. You can define a __repr__ or __str__ method in the class (there's a distinction between the two of them, but I don't remember it at the moment). That method returns a string that you want printed.

    So maybe it would be useful to add something like this to Qnode:

    
       def __repr__(self):
          return str(self.nodetype) + " - " + str(self.desc)
    
    Code (markup):
    Hope that helps. Good luck!
     
    jimrthy, Jul 27, 2006 IP