Section 3: Compound Data

 Table of Contents > Chapter 1 > Section 3 

In CPSC 110, you represented compound information by defining a struct.  In Python, there are different ways in which compound information can be represented as data.  We will use a dictionary, as this form of data plays a fundamental role in the Python programming language.

You can think of a dictionary as a set of key-value pairs.  Each key is unique within the dictionary and is used to look up a corresponding value.  This is analogous to a dictionary that provides a definition for words.  The words are the keys and the corresponding definitions are the values.  Depending on your browser, you may be able to select one of the words on this page and choose Look up in dictionary from the pop-up menu.  A definition of that word will then be presented to you.  When you do this, you select a key (a word on the page) and you are presented with the corresponding value (the definition of that word).

Consider the following example: 

>>> p = dict(x=10, y=20)
This line of code creates a new dictionary and assigns it to p.  The dictionary has two keys: x and y.  We assign the value 10 to the key x and the value 20 to the key y.   

We can now retrieve values associated with particular keys in the following way:

>>> p['x']

10
>>> p['y']

20

We can also mutate (or change) the value associated with a particular key:

>>> p['x'] = 15

>>> p['x']

15

Note that the first line of this code assigns a new value to key x, thereby overwriting (replacing) the value that was originally assigned.  Having done this, the original value cannot be retrieved.  A variable stores only the most recent value assigned to it.

We can add new keys to the dictionary by simply assigning a value to that key:

>>> p['z'] = 30
>>> p['z']
30

Finally, let's see what is currently assigned to the variable p:

>>> p
{'y':20, 'x':15, 'z':30}

Pay particular attention to the order (or lack of) in which the key-value pairs have been printed.  There is no ordering imposed on the key-value pairs - in particular, they are not stored in the order in which they were originally entered into the dictionary.  It is important to keep this in mind when designing tests involving dictionaries. 

Example

Now suppose we want to design a function that operates on a bank account.  We assume that a bank account contains information about the name of the person who holds the account and the balance on the account.  A bank account is information in the world.  We begin by designing a data definition that corresponds to this information.

We need a way to represent the name of the account holder.  Python’s built-in str data type is an appropriate choice, as it can be used to represent strings of characters, such as names.  The balance will be represented using the float data type.  Recall that the How to Design Data Definitions (HtDDD) recipe consists of four parts:

  1. A types comment that describes how the information is represented as data.
  2. An interpretation that describes how data of that form should be interpreted as information.
  3. One or more examples of the data.
  4. A template for a one-argument function that consumes the data.

Application of this recipe leads us to the following data definition:

# Account is dict(name=str, balance=float)

# interp. a bank account with a name and balance

A1 = dict(name='Joe', balance=212.23)

A2 = dict(name='Chris', balance=-12.34)

# def fn_for_acc(a):
#    return ...a['name'] ...a['balance']

The types comment on the first line specifies how information in the world (in this case a bank account) will be represented as data in our program.  It states that a bank account has a name of type str and a balance of type float.  We follow that with an interpretation statement which indicates how data of this type will be interpreted as information in the world. 

We then provide examples.  In this case, A1 is a bank account owned by Joe with a balance of $212.23 while A2 is owned by Chris and is overdrawn by $12.34. 

Finally, we provide the template for a one-argument function that consumes an account.  The template "pulls apart" the compound data and indicates that the value produced by the function will be computed by doing something with the component parts - in this case the name and the balance. 

Now let's suppose we want to design a function that consumes an Account and determines if the account is overdrawn.  By following the HtDF recipe, we arrive at the code presented in Code Explorer 1.2.