15. Classes and Objects
Object-Oriented Programming 1
Programmer-defined types
We have learned a lot of Python's built-in types. Now let's define a new type: Point
, to represent a point in 2-D space.
There are several ways we might represent points in Python:
- We could store the coordinates separately in two variables,
x
andy
. - We could store the coordinates as elements in a list or tuple.
- We could create a new type to represent points as objects.
Creating a new type is more complicated than the other options, but it has advantages that will be apparent soon.
A programmer-defined type is also called a class. A class definition looks like this:
class Point:
"""Represents a point in 2-D space."""
The header indicates that the new class is called Point
. The body is a docstring that explains what the class is for. You can define variables and methods inside a class definition, but we will get back to that later.
The class object is like a factory for creating objects. To create a Point, you call Point
as if it were a function.
my_point = Point()
my_point
The return value is a reference to a Point object, which we assign to my_point
.
Creating a new object is called instantiation, and the object is an instance of the class.
print(type(my_point))
print(isinstance(my_point, Point))
my_point.x = 3
my_point.y = 4
The variable my_point
refers to a Point
object, which contains two attributes. Each attribute refers to a floating-point number.
print(my_point.x)
print(my_point.y)
x = my_point.y
print(x)
print(my_point.x)
The expression my_point.y
means, “Go to the object my_point
refers to and get the value of y
.” In the example, we assign that value to a variable named x
. There is no conflict between the variable x
and the attribute x
.
You can use dot notation as part of any expression. For example:
import math
print(f'({my_point.x}, {my_point.y})')
distance = math.sqrt(my_point.x**2 + my_point.y**2)
print(distance)
You can pass an instance as an argument in the usual way. For example:
def print_point(p):
print(f'({p.x}, {p.y})')
print_point(my_point)
Inside the function, p
is an alias for my_point
, so if the function modifies p
, my_point
changes.
If you are not sure whether an object has a particular attribute, you can use the built-in function hasattr:
print(hasattr(my_point, 'x'))
print(hasattr(my_point, 'z'))
class Rectangle:
"""Represents a rectangle.
attributes: width, height, corner.
"""
The docstring lists the attributes: width and height are numbers; corner is a Point object that specifies the lower-left corner.
box = Rectangle()
box.width = 100.0
box.height = 200.0
box.corner = Point()
box.corner.x = 0.0
box.corner.y = 0.0
An object that is an attribute of another object is embedded.
def find_center(rect):
p = Point()
p.x = rect.corner.x + rect.width/2
p.y = rect.corner.y + rect.height/2
return p
center = find_center(box)
print_point(center)
box.width = box.width + 50
box.height = box.height + 100
You can also write functions that modify objects.
def grow_rectangle(rect, dwidth, dheight):
rect.width += dwidth
rect.height += dheight
print(box.width)
print(box.height)
print('grow')
grow_rectangle(box, 50, 100)
print(box.width)
print(box.height)
p1 = Point()
p1.x = 3.0
p1.y = 4.0
import copy
p2 = copy.copy(p1)
print_point(p1)
print_point(p2)
p1
and p2
contain the same data, but they are not the same Point
.
print(p1 is p2)
print(p1 == p2)
Because for programmer-defined types, Python doesn’t know what should be considered equivalent. At least, not yet.
Exercise 02
Write a definition for a class named Circle
with attributes center
and radius
, where center
is a Point
object and radius
is a number.
Instantiate a Circle
object that represents a circle with its center at (150, 100) and radius 75.
Write a function named point_in_circle
that takes a Circle and a Point and returns True
if the Point lies in or on the boundary of the circle.
Write a function named rect_in_circle
that takes a Circle and a Rectangle and returns True
if the Rectangle lies entirely in or on the boundary of the circle.
Write a function named rect_circle_overlap
that takes a Circle and a Rectangle and returns True
if any of the corners of the Rectangle fall inside the circle. Or as a more challenging version, return True
if any part of the Rectangle falls inside the circle.