# CS2/examples/objects/OhHowIwish.py David MacQuigg 9-Sept-07 '''Test a biased coin, using a function with inputs that you might like to change on each run. Use comments and doctests to explain your program. >>> print "Here is a doctest" Here is a doctest ''' #** Import standard modules needed by this one. import random # Function random.random() returns a random float, # uniform in range [0.0, 1.0) #** # Section tags are helpful in a big program. def testCoin(bias=0.6, numTrials=500): '''Toss a biased coin "numTrials" times. Save the results in an external list, and return the total number of heads (an int) and the percentage of heads (a float). >>> random.seed(1234) # preset needed for a repeatable doctest >>> testCoin(bias=0.2, numTrials=200) (45, 22.55) ''' global resultList # otherwise it is only local to this function totalHeads = 0; resultList = [] for trial in range(numTrials): if random.random() < bias: # Result is Heads resultList.append('H') totalHeads += 1 else: # Result is Tails resultList.append('-') percentHeads = 100.0 * totalHeads/numTrials # 100.0 forces a float return totalHeads, percentHeads #** if __name__ == '__main__': # In Python, we call this an 'idiom' (not cruft, # and certainly not gradoo). :>) '''If this module is run as the main program (not imported into some other module), run doctests and any other tests you might like to see.''' from doctest import testmod # Test this module and all >>> docstrings testmod(verbose=True) # found in any class, function or method. def runMyTests(testValues): numTrials = 100 for tv in testValues: print 'Bias =', tv, 'expect', int(tv * numTrials) totalHeads, percentHeads = testCoin(bias=tv, numTrials=numTrials) format = "totalHeads = %5s\npercentHeads = %5.1F" print format % (totalHeads, percentHeads) testValues = [0.1, 0.3, 0.5, 0.7, 0.9] # try different biases runMyTests(testValues) if len(resultList) < 200: # careful not to overwhelm your printer :>) print resultList print ' '.join(resultList) # a string format is more compact #** '''So what is wrong with this? We have nicely encapsulated the function testCoin(), making its inputs and outputs just what we want, and keeping all the "machinery" isolated inside the function. Only the 'global' statement fails to properly encapsulate some data. We did that to avoid returning a big mess. Imagine that you might have many classes of coins, each with a different method to "toss" the coin (perhaps not even using the 'random' module). You might have to do some major surgery on testCoin(), again and again for every new "coin" that comes along. 1) Study the module Coins.py to see how a professional Python programmmer might write a bunch of Coin classes with identical interfaces. 2) Re-write testCoin() so it will work with coins in any of these classes. 3) Get rid of that global statement. If you need to save some auxiliary results, tack them onto the coin which was used to generate those results. ~''' #**~