Chapter 09
Chapter 09
Contents
• Object-Oriented Programming
• Implementing a Simple Class
• Specifying the Public Interface of a Class
• Designing the Data Representation
• Constructors
• Implementing Methods
• Testing a Class
• Problem Solving: Tracing Objects
2
4/15/2025
• Problem Solving: Patterns for Object data
• Object References
• Application: Writing a Fraction Class
Object-Oriented Programming
• You have learned structured programming
• Breaking tasks into subtasks
• Writing re-usable methods to handle tasks
• We will now study Objects and Classes
• To build larger and more complex programs
3
4/15/2025
• To model objects we use in the world
4
4/15/2025
• To overcome this problem, computer scientists invented
objectoriented programming, a programming style in which tasks
are solved by collaborating objects
• Each object has its own set of data, together with a set of methods
that act upon the data
5
4/15/2025
Objects and Programs
• You have already experienced this programming style when you used
strings, lists, and file objects. Each of these objects has a set of
methods
• For example, you can use the insert() or remove() methods to
operate on list objects
6
4/15/2025
Python Classes
• A class describes a set of objects with the same behavior.
• For example, the str class describes the behavior of all strings
• This class specifies how a string stores its characters, which methods
can be used with strings, and how the methods are implemented.
• For example, when you have a str object, you can invoke the
upper
7
4/15/2025
method:
"Hello, World".upper()
8
4/15/2025
Python Classes
• In contrast, the list class describes the behavior of objects that
can be used to store a collection of values
• This class has a different set of methods
• For example, the following call would be illegal—the list class
has no upper() method
["Hello", "World"].upper()
9
4/15/2025
• However, list has a pop() method, and the following call is
legal
["Hello", "World"].pop()
Public Interfaces
• The set of all methods provided by a class, together with a
description of their behavior, is called the public interface of the
class
10
4/15/2025
• When you work with an object of a class, you do not know how the
object stores its data, or how the methods are implemented
• You need not know how a str object organizes a character
sequence, or how a list stores its elements
• All you need to know is the public interface––which methods you
can apply, and what these methods do
11
4/15/2025
Public Interfaces
• The process of providing a public interface, while hiding the
implementation details, is called encapsulation
• If you work on a program that is being developed over a long period
of time, it is common for implementation details to change, usually
to make objects more efficient or more capable
12
4/15/2025
• When the implementation is hidden, the improvements do not
affect the programmers who use the objects
13
4/15/2025
Implementing a Simple Class
• Example:
• Tally Counter: A class that models a mechanical device that is used
to count people
• For example, to find out how many people attend a concert or
board a bus
14
4/15/2025
• What should it do?
• Increment the tally
• Get the current total
15
4/15/2025
• In Python, you don’t explicitly declare instance variables
• Instead, when one first assigns a value to an instance variable, the
instance variable is created
16
4/15/2025
• Next, we invoke methods on our object
tally.reset()
tally.click()
tally.click()
result = tally.getValue() # Result is 2
tally.click()
result = tally.getValue() # Result is 3
17
4/15/2025
Instance Variables
• An object stores its data in instance variables
• An instance of a class is an object of the class
• In our example, each Counter object has a single instance variable
named _value
• For example, if concertCounter and boardingCounter are
two objects of the Counter class, then each object has its own
_value variable
18
4/15/2025
Instance Variables
• Instance variables are part of the implementation details that
should be hidden from the user of the class
• With some programming languages an instance variable can only
be accessed by the methods of its own class
19
4/15/2025
• The Python language does not enforce this restriction
• However, the underscore indicates to class users that they should
not directly access the instance variables
20
4/15/2025
Class Methods
• The methods provided by the class are defined in the class body
• The click() method advances the _value instance variable by
self._value = self._value + 1
1
def click(self) :
Example of Encapsulation
• The getValue() method returns the current _value:
def getValue(self) :
return self._value
23
4/15/2025
Complete Simple Class Example
24
4/15/2025
Program execution
25
4/15/2025
Public Interface of a Class
26
4/15/2025
Example Public Interface
27
4/15/2025
Writing the Public Interface
##A simulated cash register that tracks the item count and the total ue.amount d
#
class CashRegister :
Class comments document the
## Adds an item to this cash register.
class and the behavior of each
# @param price: the price of this item
method
#
def addItem(self, price) :
The method declarations make up the
# Method body
public interface
of the class
28
4/15/2025
Using the Class
• After defining the class we can now construct an object:
register1 = CashRegister()
# Constructs a CashRegister object
29
4/15/2025
Using Methods
• Now that an object has been constructed, we are ready to invoke a
method:
register1.addItem(1.95) # Invokes a method.
30
4/15/2025
Accessor and Mutator Methods
31
4/15/2025
Instance Variables of Objects
• Each object of a class has a separate set of instance variables
32
4/15/2025
Designing the Data Representation
34
4/15/2025
• It is common practice among Python programmers to use names
that begin with a single underscore for private instance variables
and methods
• The single underscore serves as a flag to the class user that those
members are private
35
4/15/2025
• Typically, methods are public
• However, sometimes you have a method that is used only as a
helper method by other methods
• In that case, you should identify the helper method as private by
using a name that begins with a single underscore
Constructors
• A constructor is a method that initializes instance variables of an
object
• It is automatically called when an object is created
36
4/15/2025
# Calling a method that matches the name of the
class
# invokes the constructor
register = CashRegister()
37
4/15/2025
Default and Named Arguments
38
4/15/2025
• If no value is passed to the constructor when a BankAccount
object is created the default value will be used joesAccount =
BankAccount() # Balance is set to 0
39
4/15/2025
# Balance is set to 499.95
40
4/15/2025
Syntax: Constructors
41
4/15/2025
42
4/15/2025
Constructors: Self
• The first parameter variable of every constructor must be self
• When the constructor is invoked to construct a new object, the self
parameter variable is set to the object that is being initialized
43
Refers to the
object being
initialized
def _ _init_ _(self) :
self._itemCount = 0
self._totalPrice = 0
register = CashRegister()
4/15/2025
44
4/15/2025
Object References
register = CashRegister()
45
4/15/2025
print("Your total $", register.getTotal())
register1 = CashRegister()
register1._ _init_ _() # Bad
style
46
4/15/2025
Common Error 9.1 (2)
• The constructor can set a new CashRegister object to the cleared
state, but you should not call the constructor on an existing object.
Instead, replace the object with a new one:
register1 = CashRegister()
register1 = CashRegister() #
OK
47
4/15/2025
In general, you should never call a Python method that starts with a
double underscore. The are intended for specific internal purposes
(in this case, to initialize a newly created object).
Implementing Methods
• Implementing a method is very similar to implementing a function
except that you access the instance variables of the object in the
method body
def addItem(self, price): self._itemCount
= self._itemCount + 1
self._totalPrice = self._totalPrice +
price
Task Method
48
4/15/2025
Add the price of an item addItem(price)
49
4/15/2025
Syntax: Instance Methods
• Use instance variables inside methods of the class
• Similar to the constructor, all other instance methods must include
the self parameter as the first parameter
• You must specify the self implicit parameter when using instance
variables inside the class
50
4/15/2025
Invoking Instance Methods
• As with the constructor, every method must include the special self
parameter variable, and it must be listed first.
51
4/15/2025
• When a method is called, a reference to the object on which the
method was invoked (register1) is automatically passed to the self
parameter variable:
register1.addItem(2.95)
52
4/15/2025
Tracing The Method Call
53
4/15/2025
register1 = CashRegister() #1 New object
register1.addItem(2.95) #2 Calling method
… #3 After method
54
4/15/2025
Accessing Instance Variables
• When one method needs to call another method on the same object,
you invoke the method on the self parameter
56
4/15/2025
57
4/15/2025
Example: CashRegister.py (1)
58
4/15/2025
• Instance variables should only be defined in the constructor
• All variables, including instance variables, are created at run time
• There is nothing to prevent you from creating instance variables in any
method of a class
• The constructor is invoked before any method can be called, so any
instance variables that were created in the constructor are sure to be
available in all methods
Class Variables
59
4/15/2025
• They are a value properly belongs to a class, not to any object of the
class
• Class variables are often called “static variables”
• Class variables are declared at the same level as methods
• In contrast, instance variables are created in the constructor
60
4/15/2025
Class Variables: Example (1)
• We want to assign bank account numbers sequentially: the first
account is assigned number 1001, the next with number 1002, and so
on
• To solve this problem, we need to have a single value of
_lastAssignedNumber that is a property of the class, not any object
of the class
class BankAccount :
_lastAssignedNumber = 1000 # A class
variable def _ _init_ _(self) :
self._balance = 0
BankAccount._lastAssignedNumber =
BankAccount._lastAssignedNumber + 1
self._accountNumber =
61
4/15/2025
BankAccount._lastAssignedNumber
62
4/15/2025
Class Variables: Example (3)
• For example, the BankAccount class can define a public constant
value, such as
class BankAccount :
OVERDRAFT_FEE = 29.95
. . .
Testing a Class
63
4/15/2025
• In the long run, your class may become a part of a larger program that
interacts with users, stores data in files, and so on
• You should always test your class in isolation integrating a class into a program
• Testing in isolation, outside a complete program, is called unit testing
64
4/15/2025
Choices for Testing: The Python shell
65
4/15/2025
>>> from cashregister import CashRegister
>>> reg = CashRegister
()
>>> reg.addItem
(1.95)
>>> reg.addItem
(0.95)
>>> reg.addItem
(2.50)
>>> print(reg.getCount
())
3
>>> print(reg.getTotal
())
5.4
>>>
67
4/15/2025
Steps Performed by a Tester Program
68
4/15/2025
Example Test Program
• It runs and tests the methods of the CashRegister class
Program execution
69
4/15/2025
Test Drivers: Plan Beforehand
• Thinking about the sample program:
• We add three items totaling $5.40
• When displaying the method results, we also display messages that
describe the values we expect to see
• This is a very important step. You want to spend some time thinking
about what the expected result is before you run a test program
70
4/15/2025
• This thought process will help you understand how your program
should behave, and it can help you track down errors at an early stage
• You need to import the class you are testing (here, the
CashRegister class) into the driver module:
71
4/15/2025
1) Get an informal list of responsibilities for your objects
72
4/15/2025
• 3) Document the public interface
73
4/15/2025
4) Determine the instance variables
74
4/15/2025
• An object is manipulated through the public interface (front of the
card).
• The encapsulated data is on the back of the card
75
4/15/2025
register2 = CashRegister(7.5) # 7.5 percent sales
register2.addItem(3.95, False)
tax # Not
taxable , True) #
register2.addItem(19.
95 Taxable
77
4/15/2025
• add (addItem) self._itemCount = 0
self._totalPrice = 0.0
• clear
• getTotal def getTotal(self):
return self._totalPrice
78
4/15/2025
•
Methods Required
•
Add def clear(self):
self._itemCount = 0
• Clear
self._totalPrice = 0.0
•
Optional: getCount
def getCount(self):
return self._itemCount
79
4/15/2025
• List def addItem(self, name) :
• Constructor self._choices.append
(choice)
• Initialize to empty collection
• Methods Required
• Add
80
4/15/2025
Patterns: Managing Properties
A property of an object can be class Student :
set and retrieved def _ _init_ _
• Examples (self, aName, anId)
:
• Student:name, ID self.
_name= aName
self.
_id = anId
• Constructor
• Set a unique value def getName(self) :
• Methods Required return self.
_name
• set
def setName(self, newName) :
• get self.
_name= newName
def getId(self) :
return self.
_id
# No setId method
81
4/15/2025
Patterns: Modeling Object States
Some objects can be in one of a set of
distinct states class Fish:
NOT_HUNGRY = 0
• Example: A fish SOMEWHAT_HUNGRY = 1
• Hunger states: VERY_HUNGRY = 2
• Not Hungry
def eat(self) :
• Somewhat Hungry
self._hungry =
• Very Hungry Fish.NOT_HUNGRY
• Methods will change the state
• eat def move(self) :
if self._hungry <
• move Fish.VERY_HUNGRY :
82
4/15/2025
self._hungry =
self._hungry + 1
83
4/15/2025
Patterns: Object Position
84
4/15/2025
• Examples class Bug:
• Game object def _ _init_ _
• Bug (on a grid) (self, aRow, aColumn,
aDirection, speed) :
• Cannonball self._row = aRow
• Storing values self._column= aColumn
• Row, column, direction, self._direction=
speed. . . direction
# 0 = N, 1 = E, 2 = S, 3 = W
• Methods Required
. . .
• move
• turn def moveOneUnit(self):
if (self._direction== 0):
self._row =
self._row - 1
. . .
85
4/15/2025
Object References
• In Python, a variable does not actually hold an object
• It merely holds the memory location of an object
• The object itself is stored in another location:
Reference
Object
86
4/15/2025
Shared References
• Shared References
reg2 = reg1
87
4/15/2025
The internal values can be changed through either reference
• Checking if the data contained within objects are equal use the ==
operator:
if reg1== reg2 :
print("The objects contain the same data.")
88
4/15/2025
The None reference
• A reference may point to ‘no’ object
• You cannot invoke methods of an object via a None reference –
causes a run-time error
print(reg.getTotal()) # Runtime Error!
re
g = None
89
4/15/2025
if middleInitial None
is :
print(firstName, lastName)
else :
print(firstName, middleInitial + ".", + lastName)
90
4/15/2025
• It can clarify when instance variables are used:
91
4/15/2025
def _ _init_ _(self) :
self.clear()
reg1 = CashRegister()
93
4/15/2025
Object Lifetimes: Cleaning Up
• The object, and all of its instance variables, stays alive as long as there is
at least one reference to it.
• When an object is no longer referenced at all, it is eventually removed
by a part of the virtual machine called the “garbage collector”
94
4/15/2025
• So far we have worked with floating-point numbers but computers
store binary values, so not all real numbers can be represented
precisely
• In applications where the precision of real numbers is important, we
can use rational numbers to store exact values
• This helps to reduce or eliminate round-off errors that can occur
when performing arithmetic operations
• A rational number is a number that can be expressed as a ratio of two
integers: 7/8
• The top value is called the numerator and the bottom value, which
cannot be zero, is called the denominator
95
4/15/2025
• We want to use our rational numbers as we would use integers and
floating point values
• Thus, our Fraction class must perform the following operations:
1. Create a rational number
2. Access the numerator and denominator values, individually
3. Determine if the rational number is negative or zero
4. Perform normal mathematical operations on two rational
numbers (addition, subtraction, multiplication, division,
exponentiation)
5. Logically compare two rational numbers
6. Produce a string representation of the rational number
• The objects of the Fraction class will be immutable because none of the
operations modify the objects’ instance variables
96
4/15/2025
Required Data Attributes
• Because a rational number consists of two integers, we need two
instance variables to store those values:
self._numerator = 0
self._denominator = 1
97
4/15/2025
• Signed values
• Negative and positive rational numbers each have two forms that can
be used to specify the corresponding value
• Positive values can be indicated as 1/2 or –1/–2, and negative values
as –2/5 or 2/–5
• When performing an arithmetic operation or logically comparing two
rational numbers, it will be much easier if we have a single way to
represent a negative value
• For simplicity, we choose to set only the numerator to a negative
value when the rational number is negative, and both the numerator
and denominator will be positive integers when the rational number
is positive
98
4/15/2025
• Equivalent fractions
• For example, 1/4 can be written as 1/4, 2/8, 16/64, or 123/492
• It will be much easier to perform the operation if the number is stored
in reduced form
99
4/15/2025
The Constructor (1)
• Because Fraction objects are immutable, their values must be set
when they are created. This requires parameter variables for both
the numerator and denominator
def _ _init_ _(self, numerator, denominator) :
100
4/15/2025
• Zero denominators
• The number represents zero or a negative number
The Constructor
def _ _init_ _(self, numerator = 0, denominator = 1) :
if denominator == 0 : raise
ZeroDivisionError("Denominator cannot be zero.")
101
4/15/2025
if numerator == 0 :
self._numerator = 0
self._denominator = 1
else :
if (numerator < 0 and denominator >= 0 or
numerator >= 0 and denominator < 0) :
sign = -1
else :
sign = 1
102
4/15/2025
The Constructor
a = abs(numerator)
b =
abs(denominator)
while a % b != 0 :
tempA = a tempB
= b a = tempB b
= tempA % tempB
103
4/15/2025
self._numerator = abs(numerator)// b * sign
self._denominator = abs(denominator) //b
104
4/15/2025
Testing the Constructor
frac1 = Fraction(1, 8) # Stored as 1/8 frac2 =
Fraction(-2, -4) # Stored as 1/2 frac3 =
Fraction(-2, 4) # Stored as -1/2 frac4 =
Fraction(3, -7) # Stored as -3/7 frac5 =
Fraction(0, 15) # Stored as 0/1 frac6 =
Fraction(8, 0) # Error! exception is raised.
105
4/15/2025
Comparing Fractions (1)
• In Python, we can define and implement methods that will be
called automatically when a standard Python operator (+, *, ==, <)
is applied to an instance of the class
• For example, to test whether two fractions are equal, we could
implement a method:
• isequal() and use it as follows:
if frac1.isequal(frac2) :
print("The fractions are equal.")
107
4/15/2025
Special Methods
float() function:
108
4/15/2025
Common Special Methods
109
4/15/2025
110
4/15/2025
Common Special Methods
111
4/15/2025
112
4/15/2025
Addition of Fractions
• All of the arithmetic operations that can be performed on a
Fraction object should return the result in a new Fraction object
113
4/15/2025
Fractional Addition
• From elementary arithmetic, you know that two fractions must have a
common denominator in order to add them. If they do not have a
common denominator, we can still add them using the formula:
114
4/15/2025
Defining the Method For Addition
115
4/15/2025
den = self._denominator *
rhsValue._denominator return Fraction(num,
den)
116
4/15/2025
Logic: Less Than
• Note that a / b < c / d when d · a < b · c. (Multiply both sides with b ·
d.)
• Based on this observation, the less than operation is implemented by
the _ _lt_ _() method as follows:
117
4/15/2025
return (self._numerator * rhsValue._denominator
self._denominator *
rhsValue._numerator)
118
4/15/2025
Fraction.py
119
4/15/2025
Fraction.py
120
4/15/2025
Fraction.py
121
4/15/2025
Checking Type
• To ensure that, Python provides the built-in isinstance() function
that can be used to check the type of object referenced by a variable.
• For example, the constructor for the Fraction class requires two
integers
class Fraction :
def _ _init_ _(self, numerator, denominator) :
if (not isinstance(numerator, int)
or not isinstance(denominator,
int)) :
raise TypeError
122
4/15/2025
("The numerator and denominator must be integers.")
123
4/15/2025
Summary: Variables and Methods
• An object’s instance variables store the data required for executing its
methods
• Each object of a class has its own set of instance variables
• An instance method can access the instance variables of the object on
which it acts
• A private instance variable should only be accessed by the methods of
its own class
124
4/15/2025
• Class variables have a single copy of the variable shared among all of
the instances of the class
Summary: Method Headers, Data
• Method Headers
• You can use method headers and method comments to specify the
public interface of a class
• A mutator method changes the object on which it operates
• An accessor method does not change the object on which it
operates
• Data Representation
125
4/15/2025
• For each accessor method, an object must either store or compute
the result
• Commonly, there is more than one way of representing the data of
an object, and you must make a choice
• Be sure that your data representation supports method calls in any
order
Summary: Constructors
• A constructor initializes the object’s instance variables
• A constructor is invoked when an object is created
126
4/15/2025
• The constructor is defined using the special method name: _ _init_
_()
127
4/15/2025
Summary: Testing Classes
• A unit test verifies that a class works correctly in isolation, outside a
complete program
• To test a class, use an environment for interactive testing, or write a
tester class to execute test instructions
• Determining the expected result in advance is an important part of
testing
128
4/15/2025
Summary: Object Tracing
• Object tracing is used to visualize object behavior
• Write the methods on the front of a card, and the instance variables
on the back
• Update the values of the instance variables when a mutator method is
called
129
4/15/2025
Summary: Patterns for Classes
• An instance variable for the total is updated in methods that increase
or decrease the total amount
• A counter that counts events is incremented in methods that
correspond to the events
• An object can collect other objects in a list
• An object property can be accessed with a getter method and
changed with a setter method
130
4/15/2025
• If your object can have one of several states that affect the behavior,
supply an instance variable for the current state
131
4/15/2025
Summary: Object References
• An object reference specifies the location of an object
• Multiple object variables can contain references to the same object
• Use the is and is not operators to test whether two variables are
aliases
• The None reference refers to no object
132
4/15/2025
Summary: Defining Special Methods
133
4/15/2025