[Tutor] OOPs?

Erik Price erikprice@mac.com
Sat, 30 Mar 2002 18:16:41 -0500


On Saturday, March 30, 2002, at 04:52  PM, Kirby Urner wrote:

> I'm glad you found the code useful Erik.  I learned
> something too.  I don't usually code menu loops around
> raw_input, so this helped me explore doing that, and
> I haven't used the shelve module a lot (to achieve
> persistence -- shelve uses pickle and some generic
> dbm, whatever the system supports, in the background).

So, I rewrote your code with extra comments so that I could map what 
exactly is going on in this module.  I've attached the commented code to 
the end of this email, if you have a minute and don't mind reading the 
comments and letting me know if any of them are off-base that would be 
great -- I've made some assumptions about what some things mean.  But 
you don't have to if you're busy.  (Also, do you need to add the shebang 
line to the top of modules, or just executable scripts?)

BUT... despite that, I'm finding that I can't seem to get the code to 
work right.  I can import 'stocks' because I am firing up the 
interpreter from the directory where I keep my homemade (or tutored as 
in this case) modules.  And I can call the mainmenu() function of the 
'stocks' module.  Once there, I can call the quit() function just fine.  
But it seems that my addstock() function, or perhaps it's my getcode() 
function, aren't working right.

If I choose "1", then I am prompted for a stock name and a price -- 
seems to work great.  I enter each.  But if I choose "3" to call the 
dumphist() function, I am always greeted with the exception message from 
the try block in getcode():  "Not a legal code"  Then the main menu 
comes back again (as expected, since getcode() returned a zero).

So, looking into the getcode() function, I can see that the try block is 
failing because the value of 'stockdict[code]' cannot be assigned to the 
'stockobj' name.  Since I'm assuming my Python (2.2) program is working 
fine, it must be due to the lack of a 'stockdict[code]' element in the 
stockdict dictionary.  So I go and look to see where this is generated, 
to see if my code is erroneous there.

Well, sure enough, the stockdict dictionary is assigned its associative 
indexes and its values at the end of the addstock() function.  
Essentially, the value of the 'code' variable (which is the result of 
raw_input()) is used as the index, and the value of the element is a new 
instance of the Stock class, with 'code' (the user input), 'price' (the 
second user input), 'time' (where did this come from?), and 'asctime()' 
provided as arguments to the new instance.

Here is where I've discovered my problem.  I'm going to continue with 
this email anyway, even though just now in writing it I figured out what 
was wrong, so hopefully this lesson will go into the archives and can 
help someone else someday (I'm a big fan of being a historian when I 
can).

There is no 'time' argument to the Stock class.  I mis-typed the code 
Kirby posted as I was reading it from my mail reader into my text 
editor.  I don't like copy and paste for the same reason I don't like 
hi-liting textbooks -- too easy, and too easy to miss details.  But the 
drawbacks are that you can make mistakes, and it looks like I did.  I 
had written this line as

stockdict[code] = Stock(code, price, time, asctime())

instead of the way I should have written it

stockdict[code] = Stock(code, price, time.asctime())

Now I'm going to correct my error and test the code again....

Yup!  It works!  I assigned the stock of the company I work for a measly 
11 cents per share, and then assigned them an even lower 8 cents per 
share, then I dumped the data -- here it is:

[('$0.11', 'Sat Mar 30 18:13:30 2002'), ('$0.08', 'Sat Mar 30 18:13:57 
2002')]



Okay, so I should probably save bandwidth by not posting this now but 
it's not like there's no value in this, and I still have the questions 
from the very top... thanks in advance Kirby (and spending an hour using 
this code has really taught me a lot!).



Erik


(The following is the code I was using, including the error [this means 
that this code won't work!  There's a typo in the addstock() function]:)





#!/usr/bin/python

import time, shelve

class Stock:
	"Simple stock class, keeps history using current time"
	
	# the constructor defines some attributes of the instance
	def __init__(self, code, price, datetime):
		self.code = code
		
		# the history is a dictionary that tracks the instance's
		# prices and the datetimes of those prices
		self.history = [(price, datetime)]
		self.price = price
	
	# the newprice() method adds a new price/datetime to the
	# instance's history dictionary
	def newprice(self, price, datetime):
		self.history.append((price, datetime))
	
	# the showhist() method prints the history of the instance
	# (a dictionary)
	def showhist(self):
		return self.history

def getcode():
	"Handle cases where user inputs code we don't have"
	
	# accept a stock code
	code = raw_input("Code? > ")
	
	# check to see if stock code is in the dictionary
	try:
		stockobj = stockdict[code]
	# if it can't be done, print an error message and exit
	except:
		print "Not a legal code"
		return 0
	# if it is, return the object represented by the 'stockdict[code]'
	# element of the dictionary that keeps track of stock objects by code
	return stockobj

def addstock():
	"Add new stock, any code acceptable"
	
	# accept a stock code
	code = raw_input("Code? > ")
	# accept a price
	price = raw_input("Price? > ")	
	# assign an instance of the Stock class to 'stockdict[code]'
	stockdict[code] = Stock(code, price, time, asctime())

def update():
	# assign to 'obj' an instance of the Stock class from getcode()
	obj = getcode()
	
	# if a Stock class instance was pulled and assigned to 'obj'
	if obj:
		# accept a price
		price = raw_input("Price? > ")
		# call the newprice() method of the instance
		obj.newprice(price, time.asctime())
		# print the number of items in the instance's history dictionary
		print "%s prices on file" % len(obj.history)
		# would not have to do next step if using real dictionary
		stockdict[obj.code] = obj	# overwrites old copy

def dumphist():
	# assign to 'obj' an instance of the Stock class from getcode()
	obj = getcode()
	# if a Stock class instance was pulled and assigned to 'obj'
	if obj:
		# call the 'showhist()' method of the Stock instance 'obj',
		# and assign it to the 'hist' variable
		hist = obj.showhist()
		# print the value of the 'hist' variable
		print hist

def quit():
	# call the close() method of the stockdict object
	stockdict.close()
	print "Stocks saved"

def mainmenu():
	"Using dictionary-like syntax to do file i/o"
	
	# globalize the stockdict name
	global stockdict
	# create a 'stockdict' object and give it the current value
	# of 'shelve.open("mystocks")'
	stockdict = shelve.open("mystocks")
	# create a tuple called 'callables'
	callables = [quit, addstock, update, dumphist]
	# display the menu
	while 1:
		print \
		"""
		(1) add new stock
		(2) update price of stock
		(3) dump history of stock
		(0) save/quit
		"""
		
		# accept a selection
		sel = raw_input("Your choice: > ")
		try:
			# make sel an integer
			sel = int(sel)
			# test that sel is less than or equal to 3
			# and greater than or equal to 0
			assert int(sel) <= 3 and int(sel) >= 0
			# call whichever function is represented by sel
			callables[int(sel)]()
			if int(sel) == 0:
				break
		# if the try fails
		except:
			print "Not a legal choice"