Bundling information into objects in Python (Part 1)
Example: creating circles
To illustrate the use of objects in Python, let's start with an example. Suppose we wish to store information about circles, where for each circle we will store the radius, the x and y coordinates of the centre, and the colour.
Class design recipe
- Choose meaningful attribute names
- Choose attribute types.
- Record attribute names and types of attributes
Although the computer doesn't care what names we choose for attributes, people might. Let's use the names radius
, x
, y
, and colour
.
What types should we use? We allow radius
to be either an integer or floating point number, but since we envision the circles as drawn on a grid, say with point (0,0) at the top left corner, we insist on x
and y
being nonnegative integers. colour
we store as a string.
And to store all this information, we'll use a docstring.
Creating a class
class Circle:
"""Circle radius, x and y coordinates,
and colour
Attributes:
radius: nonneg. int or float; radius
x: nonneg. int; x-coord. of centre
y: nonneg. int; y-coord of centre
colour: string; colour of the circle
"""
The first line gives the keyword class
, the name of the class, here Circle
, and of course a colon.
The summary line of the docstring lists the information stored in each object in this class, namely the radius, x and y coordinates, and colour.
Finally, more detailed information is given about each attribute. Each line lists the name of the attribute, the type of data it is, and some information about what that data means.
Here's an example showing the syntax for printing out the docstring for a class:
print(Circle.__doc__)
This shouldn't be a surprise, as it is the same as what we use to print out a docstring for a function.
Creating a function that consumes an object
Our class definition can also include some methods for the class Circle
. To highlight how a method is and isn't like functions that we've defined earlier, let's start by considering a function that consumes a Circle
.
import math
class Circle:
"""Docstring here.
"""
We'll import the math module to use in our function, and assume that we have a class definition for a circle.
def area(circ):
"""Determines area.
"""
return math.pi * circ.radius ** 2
A function that computes the area of a circle has one parameter, the circle, and calcuates the area using the radius attribute.
Here you can see the use of dot notation to extract the value of the attribute radius. Use dot notation to extract an attribute of an object.
Creating a method
To create a method in Python is pretty much like creating a function. One difference is that the method is written inside the class definition.
Another difference is that the object has a special place in the list of parameters, the first position, and a special name, self
.
Method recipe
- Add the method definition to the class definition.
- Make the object the first parameter, with name
self
.
class Circle:
"""Docstring here.
"""
def area(self):
"""Determines area.
"""
return math.pi * self.radius ** 2
In contrast to the previous example, here the definition of area
is indented to be at the same level as the docstring for the class Circle
, and now the name of the parameter is self
instead of circ
.
As before, we use dot notation to obtain the value of the attribute, so here we use self.radius
.
print(Circle.area.__doc__)
We can print the docstring for the function by using dot notation twice, the first time to show that area is a method of Circle and the second time as we've used it before.
Initialization
class Circle:
"""Docstring here.
"""
def __init__(self, radius, x, y, colour):
"""Creates new circle
"""
self.radius = radius
self.x = x
self.y = y
self.colour = colour
There are a few specially named methods. __init__
is used to initialize an object. In case it is hard to read, __init__
consists of two underscores, i-n-i-t, and then two more underscores.
This appears inside the class definition, which we'll also show later.
As usual, self
is the first parameter. The four additional parameters specify the values to be assigned to the attributes, as you can see in the four assignment statements in the method.
I used parameter names that match the names of the attributes, but that isn't required.
To use this special method, we use the syntax for a function call, where the name of the function is the name of the class and the parameters are the four input values.
eye = Circle(10, 100, 100, "brown")
This is placed outside of the class definition.
By assigning the result to the variable eye
, we've created a object eye
with the values 10
, 100
, 100
and "brown"
.
Printing objects
class Circle:
"""Docstring here.
"""
def __str__(self):
"""Prints object
"""
return self.colour + " circle of radius " + \
str(self.radius) + " centred at (" + \
str(self.x) + "," + str(self.y) + ")"
Another special method, __str__
, also uses two pairs of underscores. __str__
is used to print an object.
Here we've chosen to produce a string describing the circle.
print(eye)
Now when we use print on the object we created using the initialization method, we see the following string:
brown circle of radius 10 centred at (100, 100)
Overview
class Circle:
"""Docstring here.
"""
def __init__(self, radius, x, y, colour):
"""Details omitted.
"""
def __str__(self):
"""Details omitted.
"""
def area(self):
"""Details omitted.
"""
eye = Circle(10, 100, 100, "brown")
print(eye)
Here's what a class definition might look like. I've left out details of the docstring for the class and the details of the functions so that you could see the definition all together at once.
Notice that the defintions of the methods are placed inside the class definition but that they are used outside the class definition.
We've already seen that we can use dot notation to extract the value of an attribute:
print(eye.radius)
We can also use it in an assignment statement to change a value of an attribute, since objects are mutable. The following is an example:
eye.radius = 30
Caution
Objects are mutable.
Using methods
Many of the data types in Python are objects, such as strings. We've actually already seen how to use a method, as we've used many string methods. The following is an example:
print("rainbow".upper())
This calls the method upper
on the string "rainbow"
, using dot notation.
If we have circle object called eye
and a method called area
, we can use also dot notation to call area
on eye
:
print(eye.area())
This is true of all methods except the two special initialization and printing methods.