OO Programming and Data Structures | CS 241

04 Prepare : Reading

Outcomes

At the end of this week, successful students will be able to:

  1. Show fluency in discussing object composition.

  2. Write programs that correctly use object composition to solve problems.

This week we will dig deeper into our understanding of using class methods, and will also introduce the concept of objects that contain other objects, which is called Composition.

More with methods

As mentioned, we will begin using more objects and methods in more complicated and involved ways. To prepare for this, from Chapter 17, please read the following sections

Object composition

In additional to the reading from the online textbook, the following will introduce you to the idea of object composition.

Objects that contain more complicated data

Consider a class for a person, it might contain their first and last name, and other information:

Person
first_name : string
last_name : string
... : ...
__init__()

Some of the other information about a person could be their home address. At first, we might decide to add street, city, and zip code directly to the person object. However there are some limitations to this. First, this address information may have other properties, methods, and behaviors that wouldn't make sense for a person such as is_PO_box(). Also, if the user has two addresses, such as a home and billing address, this would require duplicating lots of member variables.

A better approach is to have the address be a class of its own:

Address
street : string
city : string
state : string
zip_code : string
__init__()

Then, we would say that a person has an address. This relationship is know as composition and is often called a has-a relationship.

Using Composition in Python

It is quite natural to use composition in Python. There are no special constructs. Instead, you create variables, just as you normally would, but the type of data stored in that variable is a complex object, rather than a primitive. For example, a default __init__ function for the person class may look as follows:


class Person:
    def __init__(self):
        self.first_name = ""
        self.last_name = ""
        self.home_address = Address()
        self.billing_address = Address()

Then, when you create a Person object, you can access the inner objects properties by chaining together "." calls such as:


bob = Person()

bob.first_name = "Robert"
bob.last_name = "Jones"
bob.home_address.street = "123 North Street"
bob.home_address.city = "Rexburg"
...
bob.billing_address.street = "PO Box 123"

Notice that even though the home address and the billing address are both Address objects, they can and do have very different values. Just as two string variables can have different values, so can two objects of a complex type.

Working with Multiple Files

A common practice is to put separate Python classes in separate files. Then, these classes are available to use in other programs. Python has a special way of handling different files and the directories they are in, organizing them into modules. But as we get started, we'll keep it simple

Assume that you have two files person.py and address.py, and that the code in person.py needs to know about the class defined in address.py. (Please be aware that it is customary to name the class Address with a capital A, but to name the file address.py with a lowercase a.)

If these two files are in the same directory, at the top of person.py, to import the Address class, you include the following line:


from address import Address

Notice the difference in capitalization here. The first address is lowercase, because we are noting that it is "from the address.py" file that we will get the class (but we don't say ".py" because Python assumes our address file will have a .py extension). The second Address is capitalized, because it is the name of the class that we are importing from the provided file.

With that import statement in our file, we can use the code, the same as if it were written directly in our file.