Defining New Objects

In Class Definition: The class and def Statements we show the syntax for creating class definitions; we cover the use of objects in Class Use: Making New Objects. We’ll discuss the notion of attribute or instance variable in The State of Being – Instance Variables. The initial state of an object is set by a special method that we’ll look at in At The Starting Line – Setting The Initial Values. We provide some exercises in Class Definition Exercises.

Class Definition: The class and def Statements

We define our own class of objects with class statement. Since the class encapsulates instance variables as well as method functions, a class definition can get lengthy.

Here’s the simplest form for a class definition. In Python 3, this will be the norm.

class className :
    suite of method defs

Since we’re using Python 2, we’ll use this slightly more complex version.

class className ( 〈 superclass 〉 , ... ) :
    suite of method defs

The className is the name of the class. This name will be used to create new objects that are instances of the class. Traditionally, class names are capitalized and class elements (variables and methods) are not capitalized.

We’ll generally provide a superclass of object in Python 2.

The suite of defs is a series of definitions for the method functions of the class. This is indented within the class definition.

The suite of defs can contain any Python programming. Generally, we try to limit our class definition to the following things:

  • A comment string (often a triple-quoted string) that provides basic documentation on the class. This string becomes a special attribute, called __doc__. It is available via the help() function.
  • Method function definitions.
  • Sometimes, we may provide class-wide “constants” – variable definitions that provide a handy short hand name for a value that doesn’t change.

The heart of the class definition is the suite of method function definitions.

def  methodName  ( self,parameter= initializer 〉 〉  , ...  ):
    suite of statements

This definition looks just like a function definition, with two exceptions.

First, it’s indented within the class statement.

Second, each of the method functions must have a first positional argument, self, which Python uses to manage the unique object instances. When referring to any method function or instance variable of the class, the instance qualifier self. must be used.

Example Definition. Here’s an example of a class with a single method definition. This class models a real-world object, a die. Note the indentation of the class definition suite. Each method function def begins at one level of indentation; each method function’s suite is at a second level of indentation.

die.py

#!/usr/bin/env python
import random
class Die( object ):
    """Simulate a 6-sided die."""
    def roll( self ):
        """Return a random roll of a die."""
        u= random.randrange(6)
        self.value= u+1
        return self.value
  1. We imported the random module to provide the random number generator.
  2. We defined the simple class named Die. The indented suite contains the docstring and the single method function of this class.
  3. The docstring has a pithy summary of the class. As with function docstrings, the class docstring is retrieved with the help() function.
  4. We defined a single method function: roll(). The instance variable, self, provides access to any variables that are created in an instance of this class. All functions that are part of a class are provided with the instance reference as the first positional parameter.
  5. When this method is executed, is sets a local variable, u, to a random value between 0 and 5. Since this variable has no instance qualifier, it is local to the function and will vanish when the function finishes.
  6. The next statements sets a instance variable, self.value, to the random value plus 1. Since self.value, is qualified by the instance, the variable is part of the state of an object and lives as long as the object does.

The processing steps are silly, but they shows the difference between a local variable, u, that doesn’t live with the object, and an instance variable, self.value, that defines the state of the object.

Tip

Debugging a Class Definition

When we get syntax errors on a class definition, it can be in the class line or one of the internal method function definitions.

If we get a simple SyntaxError on the first line, we have misspelled class, left off a ( or ), or omitted the : that begins the suite of statements that defines the class.

If we get a syntax error further in the class definition, then our method functions aren’t defined correctly. Be sure to indent the def once (so it nests inside the class). Be sure to indent the suite of statements inside the def twice.

Class Use: Making New Objects

When we use the class name as if it were a function evaluation (for example, d= Die()), three things will happen.

  • A new object is created. The object is given a reference (named __class__) to to its class definition. This d.__class__ instance variable makes d an instance of a class. In this case, the class is Die.
  • If the class defines the __init__() method function, this is evaluated. Typically, this will initialize other instance variables.
  • The resulting object will be saved in variable d for later use.

Note the similarity between using our class definition as an object factory and the built-in factory functions like int(), float(), bool(), str(), list(), tuple(), set() and dict(). A class name is also the name of an object factory.

Let’s create and interact with two objects of our Die class. First, we’ll execute the class definition module using IDLE‘s F5 or Run menu, item Run Module.

If we are working from the command line, or using a different tool, we have to import our definitions, using from die import *.

>>> from die import *
>>> d1= Die()
>>> d2= Die()
>>> print d1.roll(), d2.roll()
1 3
>>> print d1.value, d2.value
1 3
>>> print d1, d2
<die.Die object at 0x7fd30> <die.Die object at 0x7fd50>
  1. We use our Die class to create two variables, d1, and d2; both are new objects, instances of Die.

  2. We evaluate the roll() method of d1; we also evaluate the roll() method of d2. Each of these calls sets an object’s value variable to a unique, random number. There’s a pretty good chance (1 in 6) that both values might happen to be the same. If they are, simply evaluate d1.roll() and d2.roll() again to get new values.

    We print the value variable of each object. The results aren’t too surprising, since the value attribute was set by the roll() method. This attribute will be changed the next time we evaluate the roll() method.

  3. We also ask for a representation of each object. Unless we provide a method named __str__() in our class, this is what Python reports. Note that the numbers are different, indicating that these are distinct objects, each with private instance variables.

Note that we used the class definition to make two objects, d1, and d2. The objects are the focus of our program. We have manipulators (like the roll() method) and accessors (the value attribute) for these objects.

Tip

Debugging Object Construction

Assuming we’ve defined a class correctly, there are a three of things that can go wrong when attempting to construct an object of that class.

  • The class name is spelled incorrectly.
  • You’ve omitted the () after the class name. If we say d= Die, we’ve assigned the class object, Die, to the variable d. We have to say d= Die() to use the class name as a factory and create an instance of a class.
  • You’ve got incorrect argument values for the parameters of the __init__().

If we get a NameError: name 'Hack' is not defined, then the class (Hack, in this example) is not actually defined. This could mean one of three things: our class definition had errors in the first place, our definition class name isn’t spelled the same as our object creation (either we spelled it wrong when defining the class, or spelled it wrong when using the class to create an object.) The third possible error is that we have defined the class in a module, imported it, but forgot to quality the class name with the module name.

If our class wasn’t defined, it means we either forgot to define the class, or overlooked the SyntaxError when defining it. If our class has one name and our object constructor has another name, that’s just carelessness; pick a name and stick to it. If we are trying to import our definitions, we can either qualify the names properly, or use from  module import * as the import statement.

Another common problem is using the class name without ()’s. If we say d= Die, we’ve assigned the class object (Die) to the variable d. We have to say d= Die() to create an instance of a class.

If we’ve defined our class properly, we can get a message like TypeError: __init__() takes exactly 2 arguments (1 given) when we attempt to construct an object. This means that our __init__() method function doesn’t match the object construction call that we made.

The __init__() function must have a self parameter name, and it must be first. When we construct an object, we don’t provide an argument value for the self parameter, but we must provide values for all of the other parameters after self.

If your initialization function, __init__(), doesn’t seem to work, the most likely cause is that you have misspelled the name. There are two _ before and two _ after the init.

The State of Being – Instance Variables

Each ordinary method function definition must have the instance qualifier – traditionally the variable self – as the first positional parameter. This name qualifies the instance variables and the method functions of this object.

Note

Yes, there are exceptions

The exceptions to using the self instance variable are beyond the scope of this book. C++ or Java programmers may be familiar with static methods. In Python, if you’re defining static methods or class methods, you don’t have an instance variable.

We’ll see two kinds of references to variables and functions in the suites of statements in a class.

  • Names qualified by self. When we say self.name, the variable name is bound to this object. These variables are part of the object, and have the same life as the object. The variable exists after the end of any method function evaluation.

    Similarly, the name of a function that is qualified with self refers to a method function that is part of this class definition.

  • Unqualified names. Names not qualified by self are called free. These variables are ordinary local variables that has a scope that is tied to this execution of the method function. When the function finishes, the variable will be removed.

    Similarly, the name of a function that is not qualified refers to a free function that is defined outside this class.

    A free variable may also be a reference to a global variable or function, or a builtin function.

In the following example, the method function rollMany() evaluates self.roll(). The self. qualifier shows that the roll() function is part of the Dice class.

The method function roll() evaluates random.randrange(). Since this does not use the self. qualifier, it is defined outside this class definition.

class Die( object ):
    """Simulate a 6-sided die."""
    def roll( self ):
        """Return a random roll of a die."""
        u= random.randrange(6)
        self.value= u+1
        return self.value
    def rollMany( self, n ):
        all= [ self.roll() for i in range(n) ]
        return tuple(all)

At The Starting Line – Setting The Initial Values

We’ve emphasized that the behavior of each object is declared through the method functions of the object’s class. The method functions are a formal contract between the object and its client objects, specifying what the object does.

The attributes, however, do not have formal definitions. Each object’s attributes are implemented through instance variables, which – like all Python variables – are created as needed by an assignment statement. In order to guarantee that all of the instance variables exist during the entire life of the object, it is best to initialize them by providing a method with the special name of __init__(). The __init__() method is always called automatically by Python when the object is created; we can exploit this to assure a correct initialization.

In this example, we updated our Die to add an __init__() function. This function will provide a default value for the self.value attribute.

die.py, version 2

import random
class Die( object ):
    """Simulate a 6-sided die."""
    def __init__( self ):
        """Initialize the die."""
        self.value= None
    def roll( self ):
        """Return a random roll of a die."""
        self.value = random.randrange(6) + 1
        return self.value

Bonus Questions. In the first version of Die, what would happen if we did the following?

dx = Die()
print dx.value
dx.roll()
print dx.value

Compare this with what happens when we do this with the new version of Die. Which class has better behavior?

Arguments to Control Initialization. Method functions can have parameters. All of the techniques we’ve seen for ordinary function definitions apply to method functions. We can have additional positional parameters after self, keyword parameters, default values, as well as the * and ** collections of additional parameters.

As with all method functions, the __init__() method function can accept parameters. This allows us to correctly initialize an object at the same time we are creating it. The object can begin its life in a specific state. Since we don’t call the __init__() function directly, this raises a question. How are argument values assigned to the parameter variables?

The class name becomes a factory function that makes new instances of the class. When we evaluate the class, using ()‘s, we can pass argument values to the class factory. The argument values we give to the class factory are given to the __init__() method function.

For any class, C, if we say a= C( some values ), Python acts as though we said

a= C()
a.__init__(  some values )

Example Class Definition. This next example is a class that defines a geometric point. The class provides some operations that manipulate that point. When we create a Point instance, we’ll provide an x and y coordinate. To define the point (x,y)=(3,2), we could say Point(3,2). This would, in effect, do the following for us p= Point(); p.__init__( 3, 2 ).

class Point( object ):
    """A 2-D geometric point."""
    def __init__( self, x, y ):
        """Create a point at (x,y)."""
        self.x, self.y = x, y
    def offset( self, xo, yo ):
        """Offset the point by xo parallel to the x-axis
        and yo parallel to the y-axis."""
        self.x += xo
        self.y += yo
    def offset2( self, val ):
        """Offset the point by val parallel to both axes."""
        self.offset( val, val )

Here’s an example of creating a Point at coordinates (2,3) via Point(2,3) and then manipulating that point. First we move it -1 unit on the x axis and 2 units on the y axis. Then we move it -2 on both axis.

>>> from point import Point
>>> p = Point(2,3)
>>> print p
<point.Point instance at 0x98d148>
>>> print p.x, p.y
2 3
>>> p.offset( -1, 2 )
>>> print p.x, p.y
1 5
>>> p.offset2( -2 )
>>> print p.x, p.y
-1 3

After using the offset() and offset2() manipulations, the point is now at (-1,3).

Other Special Names. In addition to the specially-named __init__() method, there are many other specially-named methods that are automatically used by Python; these special methods can simplify our programming. We’ll look at many of these special methods in New Kinds of Numbers: Fractions and Currency and Creating New Types of Collections. After __init__(), the next most important special method function name may be __str__().

The __str__() method is used to return the string representation of the object. For example, we can add this method to our Point class to return an easy-to-read string for a Point.

class Point( object ):
    # ...other methods...
    def __str__( self ):
        return "(%d,%d)" % ( self.x, self.y )

Don’t Forget self. Within a class, we must be sure to use self. in front of the function names as well as attribute names. For example, our offset2() function accepts a single value and calls the object’s offset() function using the supplied value for both x and y offsets.

Operations – Access and Manipulation

The method functions allow us to access and manipulate the instance variables of an object. The method functions create a formal interface for using the object. We can think of the method functions the way we think of the buttons on the front panel of a microwave oven. We don’t know what goes on inside the oven, but we do know that pushing certain buttons in a certain order will reheat our left-over General Tso’s Chicken.

Let’s look at an example of using our Die class.

>>> d1= Die()
>>> d1.roll()
1
>>> d1.roll()
2

In this case, we created an object, d1, which is defined by the Die class. When we say Die(), we are creating a new object, and implicitly evaluating Die.__init__() to initialize that object.

After creating an instance of Die, we then evaluated the roll() method of that instance. This method updates the instance variables, self.value, with a new random number. It also returns the value of the instance variable.

A method which returns information without changing any of the instance variables is sometimes called an accessor. A method which changes an instance variable is sometimes called a manipulator.

Tip

Debugging Class vs. Object Issues

Perhaps the biggest mistake newbies make is attempting to exercise the method functions of a class instead of a specific object. You can’t easily say Die.roll(), you’ll get the cryptic TypeError: unbound method error message. The phrase “unbound method” means that no instance was being used.

When you say d1= Die(), you are creating an instance. When you see d1.roll(), then you are asking that specific object to do its roll() operation.

Politics: Collaboration and Responsibility

The real work of a program occurs when objects collaborate. We define classes that depend on other classes; we create multiple instances of objects; objects evaluate method functions of other objects.

What we do to build up an application is to allocate specific areas of responsibility to different classes. We implement each class so that it handles just its narrow area of specialization. Each class has a formalized contract with other classes, allowing us to develop and debug each class as a separate, smaller and more manageable exercise.

We’ll start out by creating a tuple object that contains five instances of the Die class from the die.py module. We’ll use that tuple object to generate a dozen rolls of these five dice. This is the kind of thing that might form part of a simulation for multi-dice games like Yacht, Kismet, Yatzee, Zilch, Zork, Greed or Ten Thousand.

import die
my_dice= ( die.Die(), die.Die(), die.Die(), die.Die(), die.Die() )
for i in range(12):
    for d in my_dice:
        d.roll()
    print [ d.value for d in my_dice ]
  1. We imported our die.py module to get the Die class definition.

  2. We created a tuple, my_dice with five distinct objects, each an instance of die.Die.

  3. Within the outer for loop, we used an explicit loop to iterate through the Die instances in the my_dice variable.

    We evaluated the the roll() method of each individual die.Die instance.

  4. To print the results, we used a list comprehension to collect the values of the individual Die instances.

Dice and Die Collaboration. Here’s a new class, Dice, which uses instances of our Die class. We’ll put this into the die.py file, right behind our Die class definition. This leads to a file that is

die.py, version 3

import random

class Die( object ):
    ... already given ...

class Dice( object ):
    "Simulate a pair of dice."
    def __init__( self ):
        "Create the two Die objects."
        self.myDice = ( Die(), Die() )
    def roll( self ):
        "Return a random roll of the dice."
        for d in self.myDice:
            d.roll()

    def getTotal( self ):
        "Return the total of two dice."
        t= 0
        for d in self.myDice:
            t += d.value
        return t
    def getTuple( self ):
        "Return a tuple of the dice values."
        return tuple( [d.value for d in self.myDice] )
    def hardways( self ):
        "Return True if this is a hardways roll."
        return self.myDice[0].value == self.myDice[1].value
  1. We import the random module. The Die class will collaborate with the random module to simulate a single die.
  2. We define Die. See die.py, version 2.
  3. We define Dice, which will collaborate with Die to simulate a pair of dice.
  4. The __init__() method creates an instance variable, myDice, which has a tuple of two instances of the Die class. The __init__() method is often called a constructor.
  5. The roll() method changes the overall state of a given Dice object by changing the two individual Die objects it contains. This kind of method is often called a manipulator. It uses a for loop to assign each of the internal Die objects to variable d. It then calls the roll() method of each Die object. This technique is called delegation: a Dice object delegates the work to two individual Die objects.
  6. The getTotal() and getTuple() methods return information about the state of the object. These kinds of methods are often called accessors. Sometimes they are called getters because their names often start with “get”.
    • The getTotal() method computes a sum of all of the Die objects. It uses a for loop to assign each of the internal Die objects to d. It then access the value instance variable of each instance of Die.
    • The getTuple() method returns the values showing on each Die object. It uses a list comprehension to create a list of the value instance variables of each Die object. The built-in function tuple converts the list into a tuple.

A Function Which Uses Die and Dice. The following function exercises an instance of this Dice class to roll two dice a dozen times and print the results.

import die
def test2():
    x= die.Dice()
    for i in range(12):
        x.roll()
        print x.getTotal(), x.getTuple()

This function creates an instance of Dice, called x. It then enters a loop to perform a suite of statements 12 times. The suite of statements first manipulates the Dice object using its roll() method. Then it accesses the Dice object using getTotal() and getTuple() method.

Alternatives. The roll() method could also be written as

def roll( self ):
    [ x.roll() for x in self.myDice ]

This will apply the roll() method to each Die in myDice. Interestingly it also creates a list object. Since the roll() function doesn’t return a value, this list object will actually be a sequence of None values. Since it isn’t assigned to a variable, it quietly blinks out of existence and is lost forever. So, each time Dice.roll() is called a little list of None‘s is created and removed.

The getTotal() method could also be written as

def getTotal( self ):
    "Return the total of two dice."
    return sum( d.value for d in self.myDice )

Keeping Organized

Here are two additional topics: how we’ll organize our class definitions, and how to create an empty class definition.

Class Definitions. In the long run, we’ll put our class definitions in files and import them into our application programs. When doing this in IDLE, we should do the following.

  1. Define your class in a file. In the following example, we’ll put the Die in a file named die.py. Don’t be fussy and put every single class into a separate file. Often, several classes will work together; it helps if they are also in a file together.
  2. Save the file.
  3. Hit the F5 or use the Run menu, item Run Module to execute the class definition statement(s).
  4. In the Python Shell window, create instances of your objects and exercise them.
  5. When you need to make changes, go back to the window with the class definitions and make the changes. Re-import the module by cycling back to step 2, above.

We’ll expand on this technique in Modules : The unit of software packaging and assembly.

Empty Class Definitions, the pass Statement. Note that the suite of defs in a class definition is required. Sometimes, however, we don’t need any definitions; in this case we have to use the pass statement.

When we introduced creation of a specialized exception class in Raising The White Flag in Exceptional Situations, we showed how to use pass as a place-filler for the suite of defs. The pass statement is the “do nothing” place-filler; we use it when the syntax rules required a suite of defs, but we really don’t have anything to add.

Here’s an example exception definition that uses the pass statement. We want our own class of exceptions, but we don’t have any new or different processing, just a new name.

class MyError( Exception ):
    pass

Class Definition Exercises

  1. Dive Logging and Surface Air Consumption Rate.

    The Surface Air Consumption Rate is used by SCUBA divers to predict air used at a particular depth. If we have a sequence of Dive objects with the details of each dive, we can do some simple calculations to get averages and ranges for our air consumption rate.

    For each dive, we convert our air consumption at that dive’s depth to a normalized air consumption at the surface. Given depth (in feet), d, starting tank pressure (psi), s, final tank pressure (psi), f, and time (in minutes) of t, the SACR, c, is given by the following formula.

    c = \frac{33 \times (s-f)}{t \times (d+33)}

    Typically, you will average the SACR over a number of similar dives. You will want to create a Dive class with start pressure, finish pressure, time and depth. Typical values are a starting pressure of 3000, ending pressure of 700 to 1500, depth of 30 to 80 feet and times of 30 minutes (at 80 feet) to 60 minutes (at 30 feet). SACR’s are typically between 10 and 20. Your Dive class should have a function named getSACR() which returns the SACR for that dive.

    To make it a little simpler to put the data in, we’ll treat time as string of HH:MM, and use string functions to pick this apart into hours and minutes. We can save this as tuple of two integers, hours and minutes. To compute the duration of a dive, we need to normalize our times to minutes past midnight, by doing hh*60+mm. Once we have our times in minutes past midnight, the difference is number of minutes of duration for the dive. You’ll want to create a function getDuration() to do just this computation for each dive.

    class Dive(object)
    __init__(start, finish, in, out, depth)

    Initialize a Dive with the start and finish pressure in PSI, the in and out time as a string, and the depth as an integer. This method should parse both the in string and out string into time tuples of hours and minutes. The parseTime() can be used to do this for both the in time and the out time.

    Note that a practical dive log would have additional information like the date, the location, the air and water temperature, sea state, equipment used and other comments on the dive.

    __str__()

    Return a nice string representation of the dive information.

    getSACR()

    Compute the SACR value from the starting pressure, final pressure, time and depth information. The duration can be computed using the getDuration() function.

    parseTime(hhmm_string)

    Pick apart a HH:MM time and convert the strings to integers to produce a 2-tuple of hours and minutes after midnight.

    getDuration(in_time, out_time)

    Accepts two 2-tuples of hours and minutes, normalizes these to minutes past midnight, and returns the difference. This is the dive’s duration in minutes.

    We’ll want to initialize our dive log as follows:

    log = [
        Dive( start=3100, finish=1300, in="11:52", out="12:45", depth=35 ),
        Dive( start=2700, finish=1000, in="11:16", out="12:06", depth=40 ),
        Dive( start=2800, finish=1200, in="11:26", out="12:06", depth=60 ),
        Dive( start=2800, finish=1150, in="11:54", out="12:16", depth=95 ),
    ]

    Your application can then process a sequence of Dives, get the SACR for each dive, and compute the average SACR over all the dives in the dive log. Here’s a start on the final program.

    total= 0
    for d in log:
        print d, d.getSACR()
        total += d.getSACR()
    print total, len(log)
    
  1. Stock Valuation.

    A block of shares in a stock has a number of attributes, including a purchase price, purchase date, and number of shares in the block. Commonly, methods are needed to compute the total spent to buy the stock, and the current value of the stock. An investor may have multiple blocks of stock in a company; this collection is called a Position.

    Beyond a simple collection of shares are larger groupings. A Portfolio, for example, is a collection of Positions; it has methods to compute the total value of all positions of stock. We’ll look at Position and Portfolio in a subsequent exercise. For now, we’ll just lock at a block of shares.

    When we purchase stocks a little at a time, each block of shares has a different price. We want the total value of the entire set of shares, plus the average purchase price for the set of shares as a whole.

    First, define a ShareBlock class which has the purchase date, price per share and number of shares.

    class ShareBlock(object)
    __init__(self, purchDate, purchPrice, shares)

    Populate the individual instance variables with date, price and shares. We’ll define another class with the ticker symbol that can act as a container for the several of these blocks for a particular company.

    __str__(self)

    Return a nice string that shows the date, price and shares.

    getPurchValue(self)

    Computer the purchase value as the price \times shares.

    getSaleValue(self, salePrice)

    Given a salePrice, compute the sale value using the sale price in price \times shares.

    getROI(self, salePrice)

    Given a salePrice, compute the return on investment as \frac{(value_sale - value_purchase)}{value_purchase}.

    We can load our database with a piece of code the looks like the following. The first statement will create a sequence with four blocks of stock. We chose variable name that would remind us that the ticker symbols for all four is ‘GM’. The second statement will create another sequence with four blocks.

    blockGM = [
    ShareBlock( purchDate='25-Jan-2001', purchPrice=44.89, shares=17 ),
    ShareBlock( purchDate='25-Apr-2001', purchPrice=46.12, shares=17 ),
    ShareBlock( purchDate='25-Jul-2001', purchPrice=52.79, shares=15 ),
    ShareBlock( purchDate='25-Oct-2001', purchPrice=37.73, shares=21 ),
    ]
    blockEK = [
    ShareBlock( purchDate='25-Jan-2001', purchPrice=35.86, shares=22 ),
    ShareBlock( purchDate='25-Apr-2001', purchPrice=37.66, shares=21 ),
    ShareBlock( purchDate='25-Jul-2001', purchPrice=38.57, shares=20 ),
    ShareBlock( purchDate='25-Oct-2001', purchPrice=27.61, shares=28 ),
    ]
    

    We can tally the purchase price of a block, for example, as follows:

    totalGM= 0
    for s in blockGM:
        totalGM += s.getPurchValue()
    print totalGM
    

    Once we have the ShareBlock class working, we can move on to processing the entire position.

  2. Stock Position.

    In Stock Valuation, we looked at a block of stock shares. A collection of these blocks represents a position on that stock. We can define an additional class, Position, which will have an the name, symbol and a sequence of ShareBlocks for a given company.

    class Position(object)
    __init__(self, name, symbol, block_list)

    Accept the company name, ticker symbol and a collection of ShareBlock instances.

    __str__(self)

    Return a string that contains the symbol, the total number of shares in all blocks and the total purchase price for all blocks.

    getPurchValue(self)

    Sum the purchase value for each block.

    getSaleValue(self, salePrice)

    Given a salePrice, sum the sale value for each block.

    getROI(self, salePrice)

    Given a salePrice, compute the return on investment as \frac{(value_sale - value_purchase)}{value_purchase}. This is an ROI based on an overall yield.

    We can create our Position objects with the following kind of initializer. This creates a sequence of three individual Position objects; one has a sequence of GM blocks, one has a sequence of EK blocks and the third has a single CAT block.

    portfolio= [
        Position( "General Motors", "GM", blocksGM ),
        Position( "Eastman Kodak", "EK", blocksEK )
        Position( "Caterpillar", "CAT",
            [ ShareBlock( purchDate='25-Oct-2001',
                purchPrice=42.84, shares=18 ) ] )
    ]

    You can now write a main program that writes some simple reports on each Position object in the portfolio, and the overall portfolio. This report should display the individual blocks purchased. This should be followed with a total price paid, and then the overall average price paid (the total paid divided by the total number of shares).

  1. Statistics Library.

    We can create a class which holds a sequence of samples. This class can have functions for common statistics on the object’s sequence of samples.

    For additional details on these algorithms, see the exercises in Doubles, Triples, Quadruples : The tuple and the exercises in Common List Design Patterns.

    class Samples(object)
    __init__(self, sequence)

    Save a sequence of samples in an instance variable. It could, at this time, also precompute a number of useful values, like the sum, count, min and max of this set of data.

    __str__(self)

    Return a summary of the data. An example is a string like "%d values, min %g, max %g, mean %g" with the number of data elements, the minimum, the maximum and the mean.

    mean(self)

    Return the sum divided by the count.

    min(self)

    Return the smallest value in the sequence of data values.

    max(self)

    Return largest value in the sequence of data values.

    variance(self)

    The variance() is a more complex calculation. For each sample, compute the difference between the sample and the mean, square this value, and sum these squares. The number of samples minus 1 is the degrees of freedom. The sum, divided by the degrees of freedom is the variance.

    stdev(self)

    Return the square root of the variance.

    mode(self)

    The mode() returns the most popular of the sample values. The following algorithm can be used to locate the mode of a set of samples.

    Computing the Mode

    1. Initialize. Create an empty dictionary, freq, for frequency distribution.

    2. For Each Value. For each value, v, in the sequence of sample values.

      Unknown?. If v is not a key in freq, then add v to the dictionary with a value of 0.

      Increment Frequency. Increment the frequency count associated with v in the dictionary.

    3. Extract. Extract the dictionary items as a sequence of tuples (value, frequency).

    4. Sort. Sort the sequence of tuples in ascending order by frequency.

    5. Result. The last value in the sorted sequence is a tuple with the most common sample value and the frequency count.

Table Of Contents

Previous topic

Objects: A Retrospective

Next topic

Inheritance, Generalization and Specialization

This Page