Lecture 18

Classes

Data Aggregation and the Copy Method

The last topic we'll talk about (for now) is data aggregation. Data aggregation is when an object holds other (mutable) objects (also called compound object). Mutable objects are objects whose value can change. Most objects are mutable, the big three that we've already met are int, float, and str. When you write a class with mutable objects as fields, consider how protective you want to be with them. Do you want someone outside of the class to make changes to those fields (without going through your set methods)? In this section, we will talk about how to maintain data aggregation security.

Data aggregation security should be maintained whenever you are giving access to your fields (which are mutable objects). This can happen whenever a user:

To fix these problems, you should:

Deep copies make copies of all fields; shallow copies just create a new variable to refer to the same object. Or, to quote from Python's documentation:

How do you make a copy of an object? That depends on the object. The data types Python provides are often as easy as passing the object into the datatype's function, e.g.:

Since ints, floats, and strings are immutable, you don't need to worry about using int, float, or str to make copies of them.

For other data types, you should look up the class's documentation and learn how to make a copy. There might be a copy method and/or it might support the copy module. To help users, provide a copy method. For our Person class, the changes in bold red show how to maintain data aggregation security:

class Person:
    def __init__(self, name, age, birthday):
        self.set_name(name)
        self.set_age(age)
        self.__set_birthday(birthday)
    
    def set_name(self, name):
        self.__name = name
    
    def get_name(self):
        return self.__name
    
    name = property(get_name, set_name)
    
    def set_age(self, age):
        if not isinstance(age, int):
            raise TypeError('age must be a positive int')
        elif age < 0:
            raise ValueError('age must be a positive int')
        
        self.__age = age
    
    def get_age(self):
        return self.__age
    
    age = property(get_age, set_age)
    
    def __set_birthday(self, birthday):
        #some validation code to ensure birthday is a valid date should be here
        self.__birthday = birthday
    
    def get_birthday(self):
        return self.__birthday
    
    birthday = property(get_birthday)
    
    def get_homeplanet(self):
        return 'Earth'
    
    home_planet = property(get_homeplanet)
    
    def __eq__(self, other):
        if self.name == other.name and self.birthday == other.birthday and self.age == other.age:
            return True
        else:
            return False
    
    def copy(self):
        return Person(self.name, self.age, self.birthday)

Note that since all of the fields are immutable, we don't need to worry about fixing the get/set methods.

See aggregation_ex.py for where fixing set/get methods becomes important.

Data aggregation security is not always a concern. For example, if the Person class maintained a list of the person's children, it probably makes sense to save the Person object and to return that object instead of making a copy of it.

<< Previous Notes Daily Schedule Next Notes >>