0% found this document useful (0 votes)
12 views133 pages

Chapter 09

Chapter Nine introduces the concepts of objects and classes in object-oriented programming, emphasizing the importance of encapsulation, instance variables, methods, and constructors. It provides guidance on designing, implementing, and testing classes, using examples like a Tally Counter and a Cash Register. The chapter also covers the significance of public interfaces and the role of object references in programming.

Uploaded by

menumina7
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
12 views133 pages

Chapter 09

Chapter Nine introduces the concepts of objects and classes in object-oriented programming, emphasizing the importance of encapsulation, instance variables, methods, and constructors. It provides guidance on designing, implementing, and testing classes, using examples like a Tally Counter and a Cash Register. The chapter also covers the significance of public interfaces and the role of object references in programming.

Uploaded by

menumina7
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 133

Chapter Nine

OBJECTS AND CLASSES


Chapter Goals
• To understand the concepts of classes, objects and encapsulation
• To implement instance variables, methods and constructors
• To be able to design, implement, and test your own classes
• To understand the behavior of object references

May 17, 2013


In this chapter, you will learn how to discover,
specify, and implement your own classes, and how
to use them in your programs.

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

A class describes objects with the same behavior.


For example, a Car class describes all passenger vehicles that
have a certain capacity and shape.

Objects and Programs


• You have learned how to structure your programs by decomposing
tasks into functions
• Experience shows that it does not go far enough
• It is difficult to understand and update a program that consists of a
large collection of functions

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()

String object Method of class String

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

Using the Counter Class


• First, we construct an object of the class (object construction will be
covered shortly):

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

tally = Counter() # Creates an instance

Using the Counter Class

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) :

• A method definition is very similar to a function with these


exceptions:
• A method is defined as part of a class definition
• The first parameter variable of a method is called self
21
4/15/2025
Class Methods and Attributes
• Note how the click() method increments the instance variable
_value

• Which instance variable? The one belonging to the object on


which the method is invoked
• In the example below the call to click() advances the _value
variable of the concertCounter object
• No argument was provided when the click() method was
called even though the definition includes the self parameter
variable

• The self parameter variable refers to the object on which the


method was invoked concertCounter in this example
22
4/15/2025
concertCounter.click()

Example of Encapsulation
• The getValue() method returns the current _value:
def getValue(self) :
return self._value

• This method is provided so that users of the Counter class can


find out how many times a particular counter has been clicked
• A class user should not directly access any instance variables
• Restricting access to instance variables is an essential part of
encapsulation

23
4/15/2025
Complete Simple Class Example

24
4/15/2025
Program execution

25
4/15/2025
Public Interface of a Class

• When you design a class, start by specifying the public interface


of the new class
• What tasks will this class perform?
• What methods will you need?
• What parameters will the methods need to receive?

26
4/15/2025
Example Public Interface

• Example: A Cash Register Class


Task Method
Add the price of an item addItem(price)
Get the total amount owed getTotal()

Get the count of items purchased getCount()

Clear the cash register for a new sale clear()


• Since the ‘self’ parameter is required for all methods it was
excluded for simplicity

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

## Gets the price of all items in the current sale.


# @return the total price
#
def getTotal(self):
...

The data and method bodies make up the


private implementation
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

• This statement defines the register1 variable and initializes it


with a reference to a new 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

• Many methods fall into two categories:


1) Accessor Methods: 'get' methods
• Asks the object for information without changing it •
Normally returns the current value of an attribute
def getTotal(self):
def getCount(self):

2) Mutator Methods: 'set' methods


• Changes values in the object
• Usually take a parameter that will change an instance variable
def addItem(self, price):
def clear(self):

31
4/15/2025
Instance Variables of Objects
• Each object of a class has a separate set of instance variables

The values stored in instance


variables make up the state of
the object.

32
4/15/2025
Designing the Data Representation

• An object stores data in instance variables


• Variables declared inside the class
• All methods inside the class have access to them
• Can change or access them
• What data will our CashRegister methods need?

Task Method Data Needed

Add the price of an item addItem(price) total, count

Get the total amount owed getTotal() total


Get the count of items purchased getCount() count
Clear cash register for a new sale clear() total, count
An object holds instance variables that are accessed by methods
33
4/15/2025
Programming Tip 9.1
• All instance variables should be private and most methods should
be public
• Although most object-oriented languages provide a mechanism to
explicitly hide or protect private members from outside access,
Python does not

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

Programming Tip 9.1


• You should always use encapsulation, in which all instance
variables are private and are only manipulated with methods

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()

• Python uses the special name _ _init_ _ for the constructor


because its purpose is to initialize an instance of the class:
def _ _init_ _(self) :
self._itemCount = 0
self._totalPrice = 0

37
4/15/2025
Default and Named Arguments

• Only one constructor can be defined per class


• But you can a define constructor with default argument values that
simulate multiple definitions class BankAccount :
def __init__(self, initialBalance = 0.0) :
self._balance = initialBalance

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

Default and Named Arguments

• If a value is passed to the constructor that value will be used


instead of the default one
joesAccount = BankAccount(499.95)

39
4/15/2025
# Balance is set to 499.95

• Default arguments can be used in any method and not just


constructors

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()

After the constructor ends this is a


reference to the newly created object

4/15/2025

44
4/15/2025
Object References
register = CashRegister()

After the constructor ends this is a reference to the


newly created object

• This reference then allows methods of the object to be invoked

45
4/15/2025
print("Your total $", register.getTotal())

Call the method


through the
reference

Common Error 9.1 (1)


• After an object has been constructed, you should not directly call the
constructor on that object again:

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)

Get the total amount owed getTotal()

Get the count of items purchased getCount()

Clear the cash register for a new sale clear()

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)

def addItem(self, price):

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

def addItem(self, price):


self._itemCount =
self._itemCount + 1
self._totalPrice =
self._totalPrice + price

54
4/15/2025
Accessing Instance Variables

• To access an instance variable, such as _itemCount or


_totalPrice, in a method, you must access the variable name
through the self reference
• This indicates that you want to access the instance variables of the
object on which the method is invoked, and not those of some other
CashRegister object

• The first statement in the addItem() method is


self._itemCount = self._itemCount + 1

Accessing Instance Variables

• Which _itemCount is incremented?


55
4/15/2025
• In this call, it is the _itemCount of the register1 object.

Calling One Method Within Another

• When one method needs to call another method on the same object,
you invoke the method on the self parameter

def addItems(self, quantity, price) :


for i in range(quantity) :
self.addItem(price)

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

Class Variables: Example (2)


• Every BankAccount object has its own _balance and
_accountNumber instance variables, but there is only a single copy of
the
_lastAssignedNumber variable

• That variable is stored in a separate location, outside any BankAccount


object
• Like instance variables, class variables should always be private to
ensure that methods of other classes do not change their values.
However, class constants can be public

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
. . .

• Methods from any class can refer to such a constant as


BankAccount.OVERDRAFT_FEE

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

• Some interactive development environments provide access to the


Python shell in which individual statements can be executed
• You can test a class simply by constructing an object, calling methods,
and verifying that you get the expected return values

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
>>>

Choices for Testing: Test Drivers

• Interactive testing is quick and convenient but it has a drawback


• When you find and fix a mistake, you need to type in the tests again
• As your classes get more complex, you should write tester programs
66
4/15/2025
• A tester program is a driver module that imports the class and contains
statements to run methods of your class

67
4/15/2025
Steps Performed by a Tester Program

1. Construct one or more objects of the class that is being tested


2. Invoke one or more methods
3. Print out one or more results
4. Print the expected results
5. Compare the computed results with the expected

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

Test Drivers: Using Modules

• You need to import the class you are testing (here, the
CashRegister class) into the driver module:

from cashregister import CashRegister

• The specific details for running the program depend on your


development environment

Steps to Implementing a Class

71
4/15/2025
1) Get an informal list of responsibilities for your objects

• There is a hidden responsibility as well. We need to be able to find out


how much money is in the account.

Steps to Implementing a Class


• 2) Specify the public interface

72
4/15/2025
• 3) Document the public interface

Steps to Implementing a Class

73
4/15/2025
4) Determine the instance variables

5) Implement constructors and methods

6) Test your class


Problem Solving: Tracing Objects

• Use an Index card for each object:

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

Mutator Methods and Cards


• As mutator methods are called, keep track of the value of instance
variables

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

Problem Solving: Patterns for Object Data

• Common patterns when designing instance variables


• Keeping a Total
• Counting Events
76
4/15/2025
• Collecting Values
• Managing Object Properties
• Modeling Objects with Distinct States
• Describing the Position of an Object

Patterns: Keeping a Total


• Examples
class CashRegister :
• Bank account balance def addItem(self, price):
• Cash Register total self._itemCount =
• Car gas tank fuel level self._itemCount + 1
self._totalPrice =
• Variables needed self._totalPrice + price
• totalPrice
• Methods Required def clear(self):

77
4/15/2025
• add (addItem) self._itemCount = 0
self._totalPrice = 0.0
• clear
• getTotal def getTotal(self):
return self._totalPrice

Patterns: Counting Events


• Examples
class CashRegister:

Cash Register items def addItem(self, price):

Bank transaction fee self._itemCount =
self._itemCount + 1

Variables needed self._totalPrice =

itemCount self._totalPrice + price

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

Patterns: Collecting Values


• Examples
• Multiple choice question class Cart:
• Shopping cart def _ _init_ _(self) :
self._choices = []
• Storing values

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:

reg1 = CashRegister The constructor returns a reference to the new


object, and that reference is stored in the reg1
variable.

Reference

Object

86
4/15/2025
Shared References

• Multiple object variables may contain references to the same object


(‘aliases’)
• Single Reference
reg1 = CashRegister

• Shared References
reg2 = reg1

87
4/15/2025
The internal values can be changed through either reference

Testing if References are Aliases

• Checking if references are aliases, use the is or the not is operator:


if reg1 is reg2 : print("The
variables are aliases.")
if reg1 is not reg2 :
print("The variables refer to different objects.")

• 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

• To test if a reference is None before using it:

89
4/15/2025
if middleInitial None
is :
print(firstName, lastName)
else :
print(firstName, middleInitial + ".", + lastName)

middleInitial = None # No middle initial

The self reference


• Every method has a reference to the object on which the method was
invoked, stored in the self parameter variable
• It is a reference to the object the method was invoked on:

90
4/15/2025
• It can clarify when instance variables are used:

def addItem(self, price):


self.itemCount = self.itemCount + 1
self.totalPrice = self.totalPrice + price

Using self to Invoke Other Methods

• You can also invoke a method on self:

91
4/15/2025
def _ _init_ _(self) :
self.clear()

• In a constructor, self is a reference to the object that is being


constructed
• The clear() method is invoked on that object

Passing self as a Parameter


• Suppose, for example, you have a Person class with a method
likes(self, other) that checks, perhaps from a social network,
whether a person likes another

def isFriend(self, other) :


return self.likes(other) and other.likes(self)
92
4/15/2025
Object Lifetimes: Creation
• When you construct an object with a constructor, the object is
created, and the self variable of the constructor is set to the
memory location of the object
• Initially, the object contains no instance variables.
• As the constructor executes statements such as instance variables are
added to the object self._itemCount = 0

• Finally, when the constructor exits, it returns a reference to the


object, which is usually captured in a variable:

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”

reg1 = CashRegister() # New object referenced by


reg1 reg1 = CashRegister()
# Another object referenced by reg1
# First object will be garbage collected

Writing a Fraction Class

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

Designing the Fraction Class

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

• At no time should the rational number be converted to a floating-point


value or we will lose the precision gained from working with rational
numbers

Representing Values Equivalently

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

Representing Values Equivalently

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) :

• The method must check for special cases:

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.")

Comparing Fractions (2)


106
4/15/2025
• Of course, we would prefer to use the operator ==
• This is achieved by defining the special method:
_ _eq_ _():

def _ _eq_ _(self, rhsValue) :


return (self._numerator == rhsValue.numerator
and self._denominator ==
rhsValue.denominator)

• Automatically calls this method when we compare two


Fraction objects using the == operator:

if frac1 == frac2 : # Calls frac1._ _eq_ _(frac2)


print("The fractions are equal.")

107
4/15/2025
Special Methods

• Some special methods are called when an instance of the class is


x = float(frac1)
passed to a built-in function. For example, suppose you attempt to
convert a Fraction object to a floating point number using the

float() function:

• Then the _ _float_ _() special method is called.


• Here is a definition of that method:
def _ _float_ _(self) :
return self._numerator / self._denominator

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

• For example, when the statement below is executed, frac1 should


be added to frac2 and the result returned as a new Fraction
object that is assigned to the newFrac variable
newFrac = frac1 + frac2

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

def _ _add_ _(self, rhsValue) :


num = (self._numerator *
rhsValue._denominator + self._denominator
* rhsValue._numerator)

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:

def _ _lt_ _(self, rhsValue) :

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.")

Summary: Classes and Objects


• A class describes a set of objects with the same behavior
• Every class has a public interface: a collection of methods through
which the objects of the class can be manipulated
• Encapsulation is the act of providing a public interface and hiding
the implementation details
• Encapsulation enables changes in the implementation without
affecting users of a class

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_
_()

• Default arguments can be used with a constructor to provide different


ways of creating an object

Summary: Method Implementation

• The object on which a method is applied is automatically passed to


the self parameter variable of the method
• In a method, you access instance variables through the self
parameter variable

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

Summary: Patterns for Classes


• To model a moving object, you need to store and update its position

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

• To use a standard operator with objects, define the corresponding


special method
• Define the special _ _repr_ _() method to create a string
representation of an object

133
4/15/2025

You might also like