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:


Fig. (1)

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.


Fig. (2)

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.


Fig. (3)

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.