Press enter to skip the top menu

Python Programming

Encapsulation

Learning Outcomes

On completion of this page you will know how to configue a class so that external code cannot corrupt its contents. In our example this will be achieved by ensuring that incoming data fall between given values and any data items outside of those values will be rejected.

Go to top

Introduction

On the previous page we made some attempts to protect the internal data of the class from being corrupted accidentally or deliberately by external code. The single measure we have taken towards this goal has been to make the class' data members private so that only methods within the class could access them and change their values.

Of course programming code does not exist in isolation - it also requires data to operate on. Also the data a program operates on is generally in a specific format or within a range of values. In our case that data for the class' object to operate on is supplied while the object is being created as shown in lines 80 to 84 below. Our previous example had no means of either eliminating or rejecting inappropriate data. Therefore we still have no protection against someone creating an object of the class Employee as follows: myPay = Employee("135531", "Joe Bloggs", 5000, 6000) and then doing a disappearing act before the crime was discovered.

How do we protect our data from situations similar to this? The answer is to restrict the values for hours and rate to acceptable ranges.

Go to top

Restricting data to allowed ranges

For our example here we will assume that the class we have been dealing with up to now is aimed at being used by an organisation that pays its employees weekly and also pays them at an hourly rate rather than a fixed salary. We will also assume the following policies:

From the above we can deduce that for any object the value for hours must be in the range 5 to 60 and the value for the hourly rate must be in the range 20 to 80.

We shall now examine Listing 2 below to see how those controls are implemented.

Go to top

Encapsulation Example

Listing 1
                        class Employee:
                            __strIRD=""
                            __strName=""
                            __floatHours=0.0
                            __floatRate=0.0
                            __floatGross=0.0
                            __floatTax=0.0
                            __floatNet=0.0
                            __blnValidData=False
                        
                            def __init__(self, ird, nme, hrs, rt):
                                if hrs >= 5 and hrs <= 60 and rt >=20 and rt <= 80:
                                    self.__strIRD = ird
                                    self.__strName = nme
                                    self.__floatHours = hrs
                                    self.__floatRate = rt
                                    self.__blnValidData = True
                                    self.__calculate()
                        
                            def irdValue(self):
                                if self.__blnValidData:
                                    return self.__strIRD
                                else:
                                    return ""
                            
                            def nameValue(self):
                                if self.__blnValidData:
                                    return(self.__strName)
                                else:
                                    return "" 
                        
                            def hoursValue(self):
                                if self.__blnValidData:
                                    return self.__floatHours
                                else:
                                    return -1 
                        
                            def rateValue(self):
                                if self.__blnValidData:
                                    return self.__floatRate
                                else:
                                    return -1
                            
                            def grossValue(self):
                                if self.__blnValidData:
                                    return self.__floatGross
                                else:
                                    return -1
                        
                            def taxValue(self):
                                if self.__blnValidData:
                                    return self.__floatTax
                                else:
                                    return -1
                        
                            def netValue(self):
                                if self.__blnValidData:
                                    return self.__floatNet
                                else:
                                    return -1
                        
                            def __calculate(self):
                                self.__floatGross=self.__floatHours*self.__floatRate
                                self.__floatTax=self.__floatGross*0.25
                                self.__floatNet=self.__floatGross-self.__floatTax
                        	
                            def toString(self):
                                if self.__blnValidData:
                                    strOutput = "IRD is " + self.__strIRD + "\n"
                                    strOutput += "Name is " + self.__strName + "\n"
                                    strOutput += "Hours worked is " + str(self.__floatHours) + "\n"
                                    strOutput += "Hourly rate is " + str(self.__floatRate) + "\n"
                                    strOutput += "Gross is " + str(self.__floatGross) + "\n"
                                    strOutput += "Tax is " + str(self.__floatTax) + "\n"
                                    strOutput += "Net is " + str(self.__floatNet) + "\n"
                                else:
                                    strOutput = "Invalid data supplied to either hours or rate"
                                return strOutput
                        		
                        payroll = Employee("123123","Tom Thumb", 20, 40)
                        payroll1 = Employee("234234", "Dick Turpin", 4, 30)
                        payroll2 = Employee("345345", "Harry Potter", 25, 200)
                        payroll3 = Employee("456456", "Sam Saw", 25, 20)
                        payroll4 = Employee("567567", "Ned Nail", 40, 30)
                        print(payroll.toString())
                        print(payroll1.toString())
                        print(payroll2.toString())
                        print(payroll3.toString())
                        print(payroll4.toString())

                   
                    

Looking at line 9 of Listing 1 we have added an extra data member __blnValidData. This is a Boolean data member which will have a value of True if the data received is correct and of False if the data is incorrect. When the values for hours and rate are received, they will be checked for being within the correct ranges. If both values are within the correct range then __blnValidData will be set to True, otherwise it i set to False. If it is True the the class' code will assume that the input and processed data are correct and the modules in the range 20 to 60 will return the values of the data items, otherwise they will return either blanks or negative values to indicate that the object contains incorrect data. Checking that the values for hours and rate takes place in the constructor, which now spans lines 11 to 18.

The number and type of the arguments passed to the constructor remain the same as the previous example but the body consists of an if construct. Here we are ensuring that the value of the hours worked must be in the range 5 to 60 inclusive and that the value of the hourly rate must be in the range 20 to 80 inclusive. Thus the condition at line 12 is that hours is greater than or equal to five and less than or equal to 60 and that the rate is greater than or equal to 20 and less than or equal to 80. If all of those conditions are met then we have correct data for our object and therefore lines 13 to 18 will be executed. Lines 13 to 16 are the same as in the previous example, i.e. the ird number, name, hours and rate are copied into the object's data members. At line 18 the data member __blnValidData is set to True to indicate to subsequent users of the object that the data contained therein is valid. Finally the method __calculate() is called at line 18.

Of course if either of the hours or rate were outside of the allowed range, the condition at line 12 would be false and thus lines 13 to 18 would have been skipped. This would have resulted in the string data members having a value of blank and the numeric data members have a value of zero. Also the data member __blnValidData would have a value of False.

The rest of the class' methods test the value of __blnValidData to determine which action to take.

The method irdValue() spans lines 20 to 24. Line 21 tests __blnValidData for being true. If it is then line 22 is executed and the value of the ird is returned, otherwise line 24 is executed and a blank is returned. The method nameValue() works exactly the same way except that the employee's name is returned if __blnValidData is true.

The method hoursValue() at lines 32 to 36 tests __blnValidData for being true. If it is then the value of the hours worked is returned at line 34, otherwise -1 is returned at line 36. The other methods in the range lines 38 to 60 work in the same way.

The method toString() spans lines 67 to 78. At line 68 the data member __blnValidData is tested for being true. If it is then lines 69 to 75 are executed, thus building up the local string variable strOutput to contain the labelled values of the object's data members, otherwise these lines are skipped and an error message is stored in strOutput line 77.

The code spanning lines 80 to 89 is external to the class. The first five lines create separate objects of the class Employee. Line 81 passes invalid data - the hours are too low. Line 82 also passes invlaid data because the rate is too high. Lines 80, 83 and 84 pass correct data.

The output is shown in Fig 1 below.

Fig 1
Go to top

Summary

The purpose of Encapsulation is to protect the integrity of an object's data. On the previous page we partially achieved this by making the data members private as well as some of the methods that processed them. On this page we extended the concept of Encapsulation to ensuring that only correct data was passed to the newly created object. This took place mainly in the object's constructor where the hours was checked for being in the range 5 to 60 and rate in the range 20 to 80. If both fall within those ranges then the object's data members are populated with the incoming data, the gross, tax and net are calculated and a Boolean variable __blnValidData is set to True

The value of this variable informs the rest of the object's methods if correct data has been received. If the data is correct then the values of the appropriate data item or items is returned to the external program, otherwise blanks or negative numbers are returned indicating invlaid data.

Go to top

Practice

Copy the code from lines 1 to 79 of Listing 1 into a code editor and save it. Now make the following changes to it:

  1. Create an object of the class Employee and pass the values "121212", "Mike Hammer", 20, 20 to it. Check the output by using the toString() method.
  2. Create another object of Employee and make up your own values to pass to it. Ensure that they are within the correct bounds. Now use the methods between lines 20 and 60 to display the values of the data methods. An example of this would be print("Employee name ", objectName.nameValue()
Go to top

Exercise

  1. What is the purpose of the double underscore in the names of the data members declared at lines 2 to 9?
  2. In the same group of variables what different data types are there?
  3. Regarding the method that spans lines 11 to 18:
    1. what is this method normally called?
    2. what purpose does the method serve?
    3. can this method be called from code outside of the class?
  4. What is the purpose of the data member __blnValidData
  5. Name the object methods that use the value of the above data member to determine what course of action to take.
  6. If this line was added to the external code of Listing 1:payroll5 = Employee("678678","Joe Bloggs",30,45) what would be the value of the internal data members once the method that spans lines 11 to 18 has finished execution
Go to top