08. Introduction to Object-Oriented Programming I#
Last time#
Modules
Packages
Exception
Assertion
Argument parser
Today#
What is object-oriented programming?#
Every object has a
data type
that defines the kinds of things that programs can do with that object.They have an internal representation through data attributes.
They have an interface for interacting with object through methods.
For example, consider a list
L = ['a', 1, 'b', 2]
.How is this list represented internally?
How to manipulate this list?
L[1] len(L) min(L) L.pop(1) L.append('34567')
Class#
Create and use your own object with
class
Create a
class object
involves…Define the name
Define the attributes of this
class object
Example:str
,tuple
,list
,dict
, …
Use a
class object
involves…Create new instance for this
class object
Define the operations or methods for this
class object
Example:len()
,(95,) + (2, 7)
,L.append(9487)
,D.keys()
, …
Syntax#
class class_name(inheritance_object*):
"""Docstrings of this class object"""
def __init__(self, optional_parameters):
"""Docstrings of this"""
<your code>
def method1(self, optional_parameters):
"""Docstrings of this method"""
<your code>
We are going to discuss the inheritance in the next lecture.
import math
class Point(object):
"""
Define a point in two dimensional space
"""
# Constructor
def __init__(self, x, y):
"""
Create instances by the constructor "__init__" and use "self" to refer to that instance
"""
self.x = x
self.y = y
def __str__(self):
"""
Define how does Python react whenever a "Point object" encounters an operand "print"
"""
return "({}, {})".format(self.x, self.y)
# Define what kind of operations you can do to a "Point object"
def cartesian(self):
"""Method 1"""
return (self.x, self.y)
# Define what kind of operations you can do to a "Point object"
def polar(self):
"""Method 2"""
radius = math.sqrt(self.x**2 + self.y**2)
phi = math.atan2(self.y, self.x)
return (radius, phi)
# Define what kind of operations you can do to a "Point object"
def distance(self, other):
"""Method 3"""
assert type(other) == Point, "All input should be a Point object."
x_diff_sq = (self.x - other.x)**2
y_diff_sq = (self.y - other.y)**2
return math.sqrt(x_diff_sq + y_diff_sq)
p1 = Point(3, 4)
p2 = Point(-3, -4)
origin = Point(0, 0)
print(p1)
print(origin)
print(p1.polar())
print(p1.distance(origin))
print(p1.distance(p2))
(3, 4)
(0, 0)
(5.0, 0.9272952180016122)
5.0
10.0
What will you get if you try these?
p3 = Point()
Point(1,0).polar()
Exercise 8.1#
Please create an object called Line2D
that aims to represent a 2D line.
It has 3 default parameters \((x_0, y_0, m)\).
It should return a message with its equation when you
print
it.
Here is your sample code:
class Line2D:
def __init__(self, x0, y0, m):
???
def __str__(self):
???
For example, you might get this message when you run the script:
line1 = Line2D(5, 3, 3)
print(line1)
A 2D line: 3x + 1y - 12
Attributes#
The data and methods that belong to the class
Instance attributes
You can think of what kind of data will it looks like in this class Ex: a
Point
should have a Cartesian coordinate (x, y)
Method attributes
You can think of what kind of functions can be work in this class only. The methods will define how Python interacts with the object.
Ex: We can transform aPoint
from Cartesian coordinate to polar coordinate via methodpolar()
we defined inPoint
.
Class attributes
You can regard it as the common attributes of all object that belongs to this class.
class Person:
# Class attribute
num_head = 1
def __init__(self, name, height=None, weight=None, gender=None):
self.name = name # Instance attribute
self.set_gender(gender)
self.set_height(height)
self.set_weight(weight)
def set_gender(self, value): # Method attribute
"""Method: set_gender"""
if value == "M":
self.gender = "Male"
elif value == "F":
self.gender = "Female"
else:
self.gender = "Secret"
def get_gender(self): # Method attribute
"""Method: get_gender"""
return self.gender
def set_height(self, value): # Method attribute
"""Method: set_height"""
if value is not None:
if value <= 0:
raise ValueError("Height cannot be less than 0.")
else:
self.height = value
def get_height(self): # Method attribute
"""Method: get_height"""
return self.height
def set_weight(self, value): # Method attribute
"""Method: set_weight"""
if value is not None:
if value <= 0:
raise ValueError("Weight cannot be less than 0.")
else:
self.weight = value
def get_weight(self): # Method attribute
"""Method: get_weight"""
return self.weight
A = Person("Abigail", gender="Female", height=170, weight=60)
B = Person("Bruce", gender="Male", height=181, weight=80)
# Instance attributes
print("Height of Abigail =", A.height)
print("Height of Bruce =", B.height)
print("Height of Abigail =", A.get_height())
print("Height of Bruce =", B.get_height())
# Class attributes
print("# of Abigail's head =", A.num_head)
print("# of Bruce's head =", B.num_head)
# Update
print("="*50)
A.set_height(175)
B.height = 185
print("Height of Abigail =", A.get_height())
print("Height of Bruce =", B.get_height())
Person.num_head = 2
print("# of Abigail's head =", A.num_head)
print("# of Bruce's head =", B.num_head)
Height of Abigail = 170
Height of Bruce = 181
Height of Abigail = 170
Height of Bruce = 181
# of Abigail's head = 1
# of Bruce's head = 1
==================================================
Height of Abigail = 175
Height of Bruce = 185
# of Abigail's head = 2
# of Bruce's head = 2
Some common special methods#
Method |
Meaning |
---|---|
|
Define how does Python react whenever this object encounters |
|
Define how does Python react whenever this object encounters |
|
Define how does Python react whenever this object encounters |
|
Define how does Python react whenever this object encounters |
|
Define how does Python react whenever this object encounters |
|
Define how does Python react whenever this object encounters |
|
Define how does Python react whenever this object encounters |
|
Define how does Python react whenever this object encounters |
|
Define how does Python get a value while iteration |
|
Define how does Python get a value while iteration |
For more special methods, please use Google or ChatGPT.
Exercise 8.2#
Please add a method attribute
check
of the objectLine2D
.It should have ability to check whether a
Point
is on the line or not.When a
Point
is not on the line, it should calculate the distance between the point and the line and return a message.
class Line2D:
def __init__(self, x0, y0, slope):
???
def __str__(self):
???
def check(self, other):
"""Check whether the point is on the line or not"""
???
For example, you might get this message when you run the script:
line1 = Line2D(5, 3, 3)
p1 = Point(6, 6)
p2 = Point(8, 7)
print(line1)
print(line1.check(p1))
print(line1.check(p2))
A 2D line: 3x + 1y - 12
The point (6, 6) is on the line.
The point (8, 7) is not on the line.
Distance: 1.5811
Iterable object and iterator#
Iterable object:
An object that can be iterating is called an iterable object.
For simplicity, an object that contains
__iter__
or__getitem__
is an iterable object.
Iterator:
An object that follows the Python Iterator Protocol is called an iterator.
For simplicity, an object that contains
__iter__
and__next__
is an iterator.
How to check?#
Use
dir()
orhasattr()
x = [9,4,8,7]
y = {"0": 87, "5": "abc", 3: "100%"}
# Check attributes by dir()
print(dir(x))
print(dir(y))
# Check attributes by hasattr()
checklist = ["__iter__", "__getitem__", "__next__"]
for item in checklist:
print("Now check: {}".format(item))
print("Does x have {}?".format(item), hasattr(x, item))
print("Does y have {}?".format(item), hasattr(y, item))
['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getstate__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
['__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__ior__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__or__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__ror__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']
Now check: __iter__
Does x have __iter__? True
Does y have __iter__? True
Now check: __getitem__
Does x have __getitem__? True
Does y have __getitem__? True
Now check: __next__
Does x have __next__? False
Does y have __next__? False
test = iter(x)
print(test)
print(test.__next__())
print(test.__next__())
print("="*50)
for i in iter(x):
print(i)
print("="*50)
for i in test:
print(i)
<list_iterator object at 0x000002610248E0E0>
9
4
==================================================
9
4
8
7
==================================================
8
7
Every time you call a for loop#

Create an iterator#
Use
__iter__
and__next__
class iterator1:
def __init__(self, max_num):
self.max_num = max_num
self.index = 0
def __iter__(self):
return self
def __next__(self):
self.index += 1
if self.index < self.max_num:
return self.index
else:
raise StopIteration
testIterator1 = iterator1(5)
for item in testIterator1:
print(item)
print("End of testIterator1, the first time")
print("="*50)
testIterator2 = iterator1(3)
for item in testIterator2:
print(item)
print("End of testIterator2")
print("="*50)
for item in testIterator1:
print(item)
print("End of testIterator1, the second time")
1
2
3
4
End of testIterator1, the first time
==================================================
1
2
End of testIterator2
==================================================
End of testIterator1, the second time
Use
__iter__
and generator (yeild
)
class iterator2:
def __init__(self, max_num):
self.max_num = max_num
def __iter__(self):
num = 0 # refresh the initial value every time you invoke the iteration
while num <= self.max_num:
yield num # output this value every loop
# yield str(num)+"!@#"
num += 1
testIterator3 = iterator2(4)
for item in testIterator3:
print(item)
print("End of testIterator3, the first time")
print("="*50)
testIterator4 = iterator2(2)
for item in testIterator4:
print(item)
print("End of testIterator4")
print("="*50)
for item in testIterator3:
print(item)
print("End of testIterator3, the second time")
0
1
2
3
4
End of testIterator3, the first time
==================================================
0
1
2
End of testIterator4
==================================================
0
1
2
3
4
End of testIterator3, the second time
Use
__getitem__
class iterator3:
def __init__(self, max_num):
self.max_num = max_num
# Python will use __getitem__ to get the value while iteration
def __getitem__(self, key):
if key <= self.max_num:
return key
else:
# raise IndexError # both can work
raise StopIteration # both can work
testIterator5 = iterator3(4)
for item in testIterator5:
print(item)
print("End of testIterator5, the first time")
print("="*50)
testIterator6 = iterator3(2)
for item in testIterator4:
print(item)
print("End of testIterator6")
print("="*50)
for item in testIterator5:
print(item)
print("End of testIterator5, the second time")
0
1
2
3
4
End of testIterator5, the first time
==================================================
0
1
2
End of testIterator6
==================================================
0
1
2
3
4
End of testIterator5, the second time
Summary#
Use
__iter__
and__next__
If there is a method attribute
__iter__
, Python will useiter()
to create an iterator.If there is a method attribute
__next__
, Python will useiter().__next__
to get value while iteration.The index of the iteration is an instance attribute.
Need to invoke the iterator object if you want to use this iterator.
Use
__iter__
and generator (yeild
)If there is a method attribute
__iter__
, Python will useiter()
to create an iterator.Although there is no method attribute
__next__
, Python can still get value viayeild
in the method attribute__iter__
.Because the index of the iteration is not an instance attribute, the initial value will be refreshed everytime Python invokes
iter()
.
Use
__getitem__
__getitem__
requires a positional argumentkey
.Everytime you call a loop, Python will create a number from 0 to \(\infty\) as the
key
of__getitem__
sequentially.Terminate the iteration when it meets the stop condition.
Don't copy this
class Line2D:
def __init__(self, x0, y0, slope) -> None:
if slope < 0:
self.coeff = (-slope, 1, slope*x0 -y0)
else:
self.coeff = (slope, -1, y0 - slope*x0)
self.eq = lambda x, y: self.coeff[0]*x + self.coeff[1]*y + self.coeff[2]
# self.eq = lambda x, y: self.m * (x - self.x0) + self.y0 - y
def __str__(self):
string = "A 2D line: {}x".format(self.coeff[0])
if self.coeff[1] < 0:
string += " + {}y".format(abs(self.coeff[1]))
else:
string += " - {}y".format(abs(self.coeff[1]))
if self.coeff[2] < 0:
string += " + {}".format(abs(self.coeff[2]))
else:
string += " - {} = 0".format(self.coeff[2])
return string
def check(self, other):
"""Check whether the point is on the line or not"""
status = True if self.eq(other.x, other.y) == 0 else False
if status:
msg = "The point ({}, {}) is on the line.".format(other.x, other.y)
else:
distance = abs(self.eq(other.x, other.y)) / (self.coeff[0]**2 + self.coeff[1]**2)**0.5
msg = "The point ({}, {}) is not on the line.\nDistance: {:.4f}".format(other.x, other.y, distance)
return msg