Python training exercise 11
Contents
Introduction
The code we've been writing so far has been a sequential set of commands and functions where variables have to be passed around from function to function whenever required. Python is, however, inherently an object-oriented programming language. This means that you can create objects that are self-sustained building blocks of code or data which you can combine to form more complex programs.
In fact we've been using objects all along; strings are objects, as are lists and dictionaries - the methods we've been using on them (for example myList.sort()) are part of these objects.
The concept of objects is not very intuitive, but we'll give you a taste here so you can see how powerful it is in practice.
Exercises
Creating a class, the template for an object
First you have to create a template for your object; this determines what an object can do once you start to use it. This template is called a class. Consider this example
# Define the MyMathOperations class # Note that we start the class name with an uppercase letter # to distinguish it from normal variables class MyMathClass: def add(self,x,y): print(x + y) def subtract(self,x,y): print(x - y) def multiply(self,x,y): print(x * y)
So within a class you define the methods add(), substract() and multiply(). The argument list for each of these has to start with self - this means it belongs to this class, and can be called from it using the .add() syntax as we used for strings, lists and dictionaries.
Using an object
When you execute the above example, nothing happens. This is because the class is not instantiated to become a working object. It's very easy to do this and then use the class; add these lines to the above example:
if __name__ == '__main__': # Instantiate the object from the class myMathObject = MyMathClass() myMathObject.add(3,5) myMathObject.subtract(5,4) myMathObject.multiply(9,3)
One big immediate advantage of doing this is that you can group similar functions (methods) together, and only have to call up the class to be able to execute them (you don't have to import all of the functions separately).
You can also connect variables to a class. You can do this when you instantiate (initialise) the class, or change/add new ones later on. Consider this modified example:
class MyNewMathClass: # This method is called when initialising the class def __init__(self,x,y): self.x = x self.y = y def add(self): return self.x + self.y def subtract(self): return self.x - self.y def multiply(self): return self.x * self.y def doSomeOperations(self): print("Numbers {} and {}".format(self.x,self.y)) print(" Adding...", self.add() print(" Subtracting...", self.subtract() print(" Multiplying...", self.multiply() print if __name__ == '__main__': # Instantiate the object from the class myMathObject = MyNewMathClass(3,5) myMathObject.doSomeOperations() myMathObject.x = 5 myMathObject.y = 4 myMathObject.doSomeOperations()
Write your own class that takes two strings on startup. It should have a method to check which letters are shared between the two strings, and another method to check which are unique in each string. You should then write out this information. [click on show more for answer) |
---|
class MyStringClass: # This method is called when initialising the class def __init__(self,string1,string2): self.string1 = string1 self.string2 = string2 # Already make sets for comparisons later... self.stringSet1 = set(string1) self.stringSet2 = set(string2) def getSharedCharacters(self): return self.stringSet1.intersection(self.stringSet2) def getUniqueCharacters(self): uniqueChars1 = self.stringSet1.difference(self.stringSet2) uniqueChars2 = self.stringSet2.difference(self.stringSet1) return (uniqueChars1,uniqueChars2) def doCharacterCheck(self): print ("Strings '{}' and '{}'".format(self.string1,self.string2)) print (" Shared characters are {}".format(", ".join(self.getSharedCharacters()))) (uniqueChars1,uniqueChars2) = self.getUniqueCharacters() print (" Unique in string1 are {}".format(", ".join(uniqueChars1))) print (" Unique in string2 are {}".format(", ".join(uniqueChars2))) print if __name__ == '__main__': # Instantiate the object from the class stringComparer = MyStringClass('myWord','otherWord') stringComparer.doCharacterCheck() |
Combining classes
The object-oriented way of programming becomes really powerful when you start to combine classes; in programming speak you can create a subclass that inherits the methods, variables, ... from one or several other classes (the superclasses). This way you can use classes as 'building blocks' to create more complex programs very quickly:
class MyInputClass: def getUserInput(self,questionString): userInput = None while not userInput: userInput = input(questionString) return userInput def getUserString(self): return self.getUserInput("Please enter a string: ") class MyStringClass: # This is a modified version of the class introduced earlier def setStringsAndCreateStringSets(self,string1,string2): self.string1 = string1 self.string2 = string2 # Already make sets for comparisons later... self.stringSet1 = set(string1) self.stringSet2 = set(string2) def getSharedCharacters(self): return self.stringSet1.intersection(self.stringSet2) class UserStringSharedCharacters(MyInputClass,MyStringClass): def getSharedCharactersFromUserStrings(self): string1 = self.getUserString() string2 = self.getUserString() self.setStringsAndCreateStringSets(string1,string2) sharedCharacters = self.getSharedCharacters() print ("Shared characters between '{}' and '{}' are {}".format(string1,string2,', '.join(sharedCharacters))) if __name__ == '__main__': userStringSharedChars = UserStringSharedCharacters() userStringSharedChars.getSharedCharactersFromUserStrings()
Convert the program to read in the sample information from exercise 9. Make a class that contains a generic CSV file reader (the comma-separated format), then another one that deals specifically with the information in this particular file, and then prints out the IDs for value ranges in the same way. Everything should be done as classes. [click on show more for answer) |
---|
import os class CsvFile: def readCsvFile(self,fileName): # Read in a .csv (comma-delimited) format file # Doublecheck if file exists if not os.path.exists(fileName): print("File {} does not exist!".format(fileName)) return None # Open the file and read the information fileHandle = open(fileName) lines = fileHandle.readlines() fileHandle.close() # Now read the information. The first line has the header information which # we are going to use to create the dictionary! fileInfoDict = {} fileInfoDict['headers'] = lines[0].strip().split(',') fileInfoDict['data'] = [] # Now read in the information for line in lines[1:]: line = line.strip() # Remove newline characters cols = line.split(',') fileInfoDict['data'].append(cols) # Return the dictionary with the file information return fileInfoDict class SampleInformationFile(CsvFile): def readFile(self,fileName): fileInfoDict = self.readCsvFile(fileName) # Reorganise generic information from file in correct way.. self.sampleInformation = {} for dataList in fileInfoDict['data']: sampleId = int(dataList[0]) self.sampleInformation[sampleId] = {} for i in range(1,len(fileInfoDict['headers'])): valueName = fileInfoDict['headers'][i] value = dataList[i] if valueName in ('pH','temperature','volume'): value = float(value) self.sampleInformation[sampleId][valueName] = value # No need to return anything; the data is stored in self.sampleInformation, and this is accessible from anywhere in this object... def getSampleIdsForValueRange(self,valueName,lowValue,highValue): # Return the sample IDs that fit within the given value range for a kind of value sampleIdList = self.sampleInformation.keys() sampleIdList.sort() sampleIdsFound = [] for sampleId in sampleIdList: currentValue = self.sampleInformation[sampleId][valueName] if lowValue <= currentValue <= highValue: sampleIdsFound.append(sampleId) return sampleIdsFound if __name__ == '__main__': sampleInfoFile = SampleInformationFile() sampleInfoFile.readFile("sampleInfo.txt") print(sampleInfoFile.getSampleIdsForValueRange('pH',6.0,7.0)) print(sampleInfoFile.getSampleIdsForValueRange('temperature',280,290)) print(sampleInfoFile.getSampleIdsForValueRange('volume',200,220)) |