Bundling information into objects in Python (Part 3)
Example: Event class
Let's create a class for events that uses the Time
class.
class Event:
"""Event given as start and end times
Methods:
__init__: initializes a new object
Attributes:
start: Time, when event starts
end: Time, when event ends
"""
As before, our docstring gives a summary and then lists the methods and attributes.
def __init__(self, start, end):
"""Initializes a new object
Preconditions:
start, end: Time objects
Parameters:
start, end: start and end times
Side effect: attributes set with values
"""
self.start = start
self.end = end
We also create an initialization method, which allows us to create an Event
object out of two Time
objects.
Creating events
gym_start = Time(5, 30)
gym_end = Time(8, 5)
gym = Event(gym_start, gym_end)
We create some times and an event. We can visualize them in the following way:
The figure shows three addresses in memory.
The address gym_start
points to a Time
object with hour
pointing to 5 and min
pointing to 30.
The address gym_end
points to a Time
object with hour
pointing to 8 and min
pointing to 5.
The address gym
points to an Event
object that consists of two addresses, start
and end
. The start
of gym
points to the same Time
object as gym_start
and the end
of gym
points to the same Time
object as gym_end
.
Copying objects
import copy
shallow = copy.copy(gym)
To copy objects, we import the copy
module, and use the copy
function in the copy
module. Here we're making a copy of gym
which we call shallow
.
The figure shows the same three addresses in memory that appeared in Figure 1.
In addition, we obtain a new address called shallow
pointing to a new Event
object, but this Event
points to the same Time
objects as gym
does. This means that any changes to the Time
objects in gym
will also result in changes in shallow
.
This is why I gave this copy the name shallow
.
Caution
An object inside the object will not be copied.
Deep copy
import copy
deep = copy.deepcopy(gym)
If instead I want everything to be copied, I can use deepcopy
, another function in the copy
module.
The figure shows the same three addresses in memory that appeared in Figure 1.
In addition, we obtain a new address called deep
pointing to a new Event
object; this Event
points to two new Time
objects, which are copies of the Time
objects in gym
. This time nothing is shared between gym
and deep
. You can see how changes to gym
will not change deep
.
Event class example
import copy
class Time:
"""Time stored as hour and minutes
Methods:
__init__: initializes a new object
__str__: prints an object
Attributes:
hour: int, 0 <= value < 24
minute: int, 0 <= value < 60
"""
def __init__(self, hour, minute):
"""Initializes a new object.
Preconditions:
hour: int, 0 <= value < 24
minute: int, 0 <= value < 60
Parameters:
hour: hour in time
minute: minutes in time
Side effect: attributes set with values
"""
self.hour = hour
self.minute = minute
def __str__(self):
"""Prints time.
Side effect: prints
"""
if self.minute < 10:
minute_word = "0" + str(self.minute)
else:
minute_word = str(self.minute)
return str(self.hour) + ":" + \
minute_word
class Event:
"""Event given as start and end times
Methods:
__init__: initializes a new object
Attributes:
start: Time, when event starts
end: Time, when event ends
"""
def __init__(self, start, end):
"""Initializes a new object
Preconditions:
start, end: Time objects
Parameters:
start, end: start and end times
Side effect: attributes set with values
"""
self.start = start
self.end = end
We load in the copy module as well as the class definitions and initialization methods for Time and Event.
gym_start = Time(5, 30)
gym_end = Time(8, 5)
gym = Event(gym_start, gym_end)
We create times, and an event.
shallow = copy.copy(gym)
print(shallow is gym)
We make a copy of the event and see that it is at a different address from the original by the following output:
False
deep = copy.deepcopy(gym)
print(deep is gym)
And we also make a deep copy. Adding the previous print statement now generates the following:
False False
gym.start.hour = 6
We now change the value of the hour
attribute in one of the Time
objects inside our Event
object. Notice how we use dot notation twice: gym.start
is a Time
object, so we can use .hour
to extract the value of the hour
attribute.
print(gym.start)
print(shallow.start)
print(deep.start)
What happens to our copies? Adding the given print statements generates the following:
False False 6:30 6:30 5:30
The shallow
copy does have a different address, but still uses the same addresses for any objects inside the Event
object. So we see that when we changed the time for gym
, we also changed the time for shallow
.
The deep
copy made new copies of everything, so it was not changed.
Style conventions
We end our discussion of classes and objects by summarizing the conventions used.
Class definition
Python conventions
- Blank line between class header and docstring
- Summary, list methods, list attributes
- Docstrings for methods, like functions
- Blank lines between docstring and methods
Leaving blanks between the docstring and the first method will make the whole class definition more readable.
Identifiers
Python conventions
- Class names capitalized
- Method names like function names
Method names are not capitalized, as they are like function names.