Introduction Programming Using Python Lecture 8
Dr. Zhang
COSC 1437 Fall 2019
Nov 21, 2019
Chapter 12 Inheritance and Class Design Review
• Suppose you will define classes to model circles, rectangles, and triangles. These classes have many common features. What is the best way to design these classes so to avoid redundancy? The answer is to use inheritance.
Objectives
• To develop a subclass from a superclass through inheritance (§12.2).
• To override methods in the subclass (§12.3).• To explore the object class and its methods (§12.4).• To understand polymorphism and dynamic binding
(§12.5).• To determine if an object is an instance of a class using
the isinstance function (§12.6).• To discover relationships among classes (§12.8).• To design classes using composition and inheritance
relationships (§§12.9-12.11).
Superclasses and Subclasses
• Sample code: GeometricObject.py,Circle.py,Rectangle.py
• TestCircleRectangle.py
GeometricObject
-color: str
-filled: bool
GeometricObject(color: str, filled:
bool)
getColor(): str
setColor(color: str): None
isFilled(): bool
setFilled(filled: bool): None
__str__(): str
The color of the object (default: white).
Indicates whether the object is filled with a color (default: false).
Creates a GeometricObject with the specified color and filled
values.
Returns the color.
Sets a new color.
Returns the filled property.
Sets a new filled property.
Returns a string representation of this object.
Circle
-radius: float
Circle(radius: float, color: str, filled:
bool)
getRadius(): float
setRadius(radius: double): None
getArea(): float
getPerimeter(): float
getDiameter(): float
printCircle(): None
Rectangle
-width: double
-height: double
Rectangle(width: float, height: float color:
string, filled: bool)
getWidth(): float
setWidth(width: float): None
getHeight(): float
setHeight(height: float): None
getArea(): float
getPerimeter(): float
Overriding Methods
• A subclass inherits methods from a superclass. Sometimes it is necessary for the subclass to modify the implementation of a method defined in the superclass. This is referred to as method overriding.
The object Class
• Every class in Python is descended from the object class. If no inheritance is specified when a class is defined, the superclass of the class is object by default.
There are more than a dozen methods defined in the object class. We discuss four methods __new__(), __init__(), __str__(), and __eq__(other) here. class ClassName:
...
Equivalent class ClassName(object):
...
The __new__, __init__ Methods
• All methods defined in the object class are special methods with two leading underscores and two trailing underscores. The __new__() method is automatically invoked when an object is constructed. This method then invokes the __init__() method to initialize the object. Normally you should only override the __init__() method to initialize the data fields defined in the new class.
The __str__ Method
• The __str__() method returns a string representation for the object. By default, it returns a string consisting of a class name of which the object is an instance and the object’s memory address in hexadecimal.
•
The __eq__ Method
• The __eq__(other) method returns True if two objects are the same. By default, x.__eq__(y) (i.e., x == y) returns False, but x.__eq__(x) is True. You can override this method to return True if two objects have the same contents.
Polymorphism• The three pillars of object-oriented programming
are encapsulation, inheritance, and polymorphism. The inheritance relationship enables a subclass to inherit features from its superclass with additional new features. A subclass is a specialization of its superclass; every instance of a subclass is also an instance of its superclass, but not vice versa. For example, every circle is a geometric object, but not every geometric object is a circle. Therefore, you can always pass an instance of a subclass to a parameter of its superclass type.
• Example code: PolymorphismDemo.py
Dynamic Binding• Dynamic binding works as follows: Suppose an object o is an
instance of classes C1, C2, ..., Cn-1, and Cn, where C1 is a subclass of C2, C2 is a subclass of C3, ..., and Cn-1 is a subclass of Cn. That is, Cn is the most general class, and C1 is the most specific class. In Python, Cn is the object class. If o invokes a method p, the JVM searches the implementation for the method p in C1, C2, ..., Cn-1 and Cn, in this order, until it is found. Once an implementation is found, the search stops and the first-found implementation is invoked.
Cn Cn-1 . . . . . C2 C1
object
Since o is an instance of C1, o is also an
instance of C2, C3, …, Cn-1, and Cn
The isinstance Function
• The isinstance function can be used to determine if an object is an instance of a class.
Case Study: A Reusable Clock
StillClock
-hour: int
-minute: int
-second: int
StillClock(container)
setCurrentTime(): None
tkinter.Canvas
-char token
+getToken
+setToken
+paintComponet
+mouseClicked
The get and set methods for these data
fields are provided in the class, but
omitted in the UML diagram for brevity.
The hour in the clock.
The minute in the clock.
The second in the clock.
Constructs a default clock for the current time,
placed inside a container.
Sets hour, minute, and second to current time.
Relationships among Classes
• Association
• Aggregation
• Composition
• Inheritance
Association
• Association represents a general binary relationship that describes an activity between two classes.
Student * 5..60 Take Teach
0..3 1
Teacher Faculty Course
class Student:
def addCourse(self,
course):
# add course to a list
class Course:
def addStudent(self,
student):
# add student to a list
def setFaculty(self, faculty):
# Code omitted
class Faculty:
def addCourse(self,
course):
# add course to a list
An association is usually represented as a data field in the class.
Association Between Same Class
• Association may exist between objects of the same class. For example, a person may have a supervisor.
Person
Supervisor
1
1
Aggregation and Composition
• Aggregation is a special form of association, which represents an ownership relationship between two classes. Aggregation models the has-a relationship. If an object is exclusively owned by an aggregated object, the relationship between the object and its aggregated object is referred to as composition.
Name Address Person
Composition Aggregation
Representing Aggregation in Classes
• An aggregation relationship is usually represented as a data field in the aggregated class.
class Name:
...
class Student:
def _init_(self, name, address)
self.name = name
self.address = address
...
}
class Address:
...
Aggregated class Aggregating class Aggregated class
Chapter 13 Files and Exception Handling
• How do you write data to a file and read the data back from a file? You need to create a file object that is associated with a physical file. This is called opening a file. The syntax for opening a file is as follows:
• file = open(filename, mode) Mode Description
'r' Open a file for reading only.
'w' Open a file for writing only.
'a' Open a file for appending data. Data are
written to the end of the file.
'rb' Open a file for reading binary data.
'wb' Open a file for writing binary data.
Write to a File
• Code Example:
writeDemo.py
file
read([number: int]): str
readline(): str
readlines(): list
write(s: str): None
close(): None
Returns the specified number of characters from the file. If the
argument is omitted, the entire remaining contents are read.
Returns the next line of file as a string.
Returns a list of the remaining lines in the file.
Writes the string to the file.
Closes the file.
Testing File Existence
Read from a File
• After a file is opened for reading data, you can use the read method to read a specified number of characters or all characters, the readline() method to read the next line, and the readlines() method to read all lines into a list.
• Code example:
readDemo.py
Append Data to a File
• You can use the 'a' mode to open a file for appending data to an existing file.
• def main():
# Open file for appending data
outfile = open("Info.txt", "a")
outfile.write("\nPython is interpreted\n")
outfile.close() # Close the input file
main() # Call the main function
Writing/Reading Numeric Data
• To write numbers, convert them into strings, and then use the write method to write them to a file. In order to read the numbers back correctly, you should separate the numbers with a whitespace character such as ' ', '\n’.
• Code example:
• WriteReadNumbers.py
WriteReadNumbers.py• from random import randint
• def main():# Open file for writing data
outfile = open("Numbers.txt", "w")for i in range(10):
outfile.write(str(randint(0, 9)) + " ")outfile.close() # Close the file
# Open file for reading datainfile = open("Numbers.txt", "r")s = infile.read()numbers = [eval(x) for x in s.split()]for number in numbers:
print(number, end = " ")infile.close() # Close the file
main() # Call the main function
File Dialogs
from tkinter.filedialog import askopenfilename
from tkinter.filedialog import asksaveasfilename
filenameforReading = askopenfilename()
print("You can read from from " + filenameforReading)
filenameforWriting = asksaveasfilename()
print("You can write data to " + filenameforWriting)
File Dialogs
File EditorFileEditor.py
Problem: Counting Each Letter in a File
• The problem is to write a program that prompts the user to enter a file and counts the number of occurrences of each letter in the file regardless of case.
• Code Example:
– CountEachLetter.py
Retrieving Data from the Web
• Using Python, you can write simple code to read data from a Website. All you need to do is to open a URL link using the urlopen function as follows:
infile = urllib.request.urlopen('http://www.yahoo.com')
import urllib.request
infile = urllib.request.urlopen('http://www.yahoo.com/index.html')
print(infile.read().decode())
Retrieving Data from the Web
• import urllib.request
def main():
• url = input("Enter an URL for a file: ").strip()
• infile = urllib.request.urlopen(url)
• s = infile.read().decode() # Read the content as string
• counts = countLetters(s.lower())
•
• # Display results
• for i in range(len(counts)):
• if counts[i] != 0:
• print(chr(ord('a') + i) + " appears " + str(counts[i])
• + (" time" if counts[i] == 1 else " times"))
• # Count each letter in the string
• def countLetters(s):
• counts = 26 * [0] # Create and initialize counts
• for ch in s:
• if ch.isalpha():
• counts[ord(ch) - ord('a')] += 1
• return counts
• main() # Call the main function
Exception Handling
• When you run the program in Listing 11.3 or Listing 11.4, what happens if the user enters a file or an URL that does not exist? The program would be aborted and raises an error. For example, if you run Listing 11.3 with an incorrect input, the program reports an IO error as shown below:
The try ... except Clause
The try ... except Clause
The try ... except Clause
• from random import randint
• def main():• # Open file for writing data• outfile = open("Numbers.txt", "w")• for i in range(10):• outfile.write(str(randint(0, 9)) + " ")• outfile.close() # Close the file
• # Open file for reading data• infile = open("Numbers.txt", "r")• s = infile.read()• numbers = [eval(x) for x in s.split()]• for number in numbers:• print(number, end = " ")• infile.close() # Close the file•
• main() # Call the main function
Raising Exceptions
• You learned how to write the code to handle exceptions in the preceding section. Where does an exception come from? How is an exception created? Exceptions are objects and objects are created from classes. An exception is raised from a function. When a function detects an error, it can create an object of an appropriate exception class and raise the object, using the following syntax:
• raise ExceptionClass("Something is wrong")• Code Example: RaiseException.py
Processing Exceptions Using Exception Objects
• You can access the exception object in the except clause.
• Code example: ProcessingExceptionObject.py
Binary IO Using Pickling
• To perform binary IO using pickling, open a file using the mode 'rb' or 'wb' for reading binary or writing binary and invoke pickle module’s dump and load functions to write and read data.
• Code example:
– BinaryIODemo.py
BinaryIODemo.py
• import pickle
• def main():• # Open file for writing binary• outfile = open("pickle.dat", "wb")• pickle.dump(45, outfile)• pickle.dump(56.6, outfile)• pickle.dump("Programming is fun", outfile)• pickle.dump([1, 2, 3, 4], outfile)• outfile.close() # Close the output file
• # Open file for reading binary• infile = open("pickle.dat", "rb")• print(pickle.load(infile))• print(pickle.load(infile))• print(pickle.load(infile))• print(pickle.load(infile))• infile.close() # Close the input file
• main() # Call the main function
Detecting End of File
• If you don’t know how many objects are in the file, how do you read all the objects? You can repeatedly read an object using the load function until it throws an EOFError exception. When this exception is raised, catch it and process it to end the file reading process.
• Code Example:
– DetectEndOfFile.py
Case Study: Address Book
• Now let us use object IO to create a useful project for storing and viewing an address book. The user interface of the program is shown below. The Add button stores a new address at the end of the file. The First, Next, Previous, and Last buttons retrieve the first, next, previous, and last addresses from the file, respectively.
Chapter 15 Recursion
• Suppose you want to find all the files under a directory that contains a particular word. How do you solve this problem? There are several ways to solve this problem. An intuitive solution is to use recursion by searching the files in the subdirectories recursively.
Computing Factorial
• factorial(0) = 1;
• factorial(n) = n*factorial(n-1);
• n! = n * (n-1)!
• Code example:
ComputeFactorial.py
ComputeFactorial.py
• def main():• n = eval(input("Enter a nonnegative integer: "))• print("Factorial of", n, "is", factorial(n))
• # Return the factorial for a specified number • def factorial(n):• if n == 0: # Base case• return 1• else:• return n * factorial(n - 1) # Recursive call
• main() # Call the main function
Computing Factorial
• factorial(3) = 3 * factorial(2)
• = 3 * (2 * factorial(1))
• = 3 * ( 2 * (1 * factorial(0)))
• = 3 * ( 2 * ( 1 * 1)))
• = 3 * ( 2 * 1)
Computing Factorial
• factorial(4) = 4 * factorial(3) • = 4 * 3 * factorial(2) • = 4 * 3 * (2 * factorial(1)) • = 4 * 3 * ( 2 * (1 * factorial(0))) • = 4 * 3 * ( 2 * ( 1 * 1))) • = 4 * 3 * ( 2 * 1) • = 4 * 3 * 2 • = 4 * 6• = 24
Characteristics of Recursion
• All recursive methods have the following characteristics:
• One or more base cases (the simplest case) are used to stop recursion.
• Every recursive call reduces the original problem, bringing it increasingly closer to a base case until it becomes that case.
• In general, to solve a problem using recursion, you break it into subproblems. If a subproblem resembles the original problem, you can apply the same approach to solve the subproblem recursively. This subproblem is almost the same as the original problem in nature with a smaller size.
Problem Solving Using Recursion
• Let us consider a simple problem of printing a message for n times. You can break the problem into two subproblems: one is to print the message one time and the other is to print the message for n-1 times. The second problem is the same as the original problem with a smaller size. The base case for the problem is n==0. You can solve this problem using recursion as follows:
Recursion vs. Iteration
• Recursion is an alternative form of program control. It is essentially repetition without a loop.
• Recursion bears substantial overhead. Each time the program calls a method, the system must assign space for all of the method’s local variables and parameters. This can consume considerable memory and requires extra time to manage the additional space.
Advantages of Using Recursion
• Recursion is good for solving the problems that are inherently recursive.