Object-Oriented Programming With Python : Inheritance (2/3)
Continuing on our Object-Oriented Programming with Python series, this is article 2 of 3 on the 2nd pillar, Inheritance. You can find the first article about Encapsulation in this series here.
Inheritance – The Second Pillar
Inheritance in OOP is the ability to have one class inherit the attributes of another class.
Inheritance is simple to implement, but arguably the most powerful pillar in OOP, providing the most value to a programmer. It is the pillar that allows for effective re-use of code organized in classes reducing the amount of lines a programmer has to write to do something useful in their program.
In case you don’t remember from the previous article in this series on encapsulation, a class is a blueprint or template that contains methods, objects and attributes. Once defined a class can be inherited by another class. At which point the child class (aka derived class or subclass) contains all the goodness that the parent class ( aka super class or base class) provides.
Here is an example of inheritance :
#!/usr/bin/env python import requests class Tuxlabs(object): def setter(self, website): if 'http' in website: self.website = website else: print 'ERROR: Unable to set website to %s.\nRequired format is: http://www.example.com' % (website) def getter(self): return self.website class Crawl(Tuxlabs): def download(self, website): self.setter(website) crawlTarget = self.getter() r = requests.get(crawlTarget) print r.text website = 'http://www.tuxlabs.com' crawl = Crawl() crawl.download(website)
The code above downloads the webpage ‘tuxlabs.com’ and outputs it’s contents to STDOUT using the ‘requests’ module.
<rant>If you are a Python developer who is not already familiar with the requests module you should be ashamed of yourself 🙂 It is the most fantastically simple implementation of Python HTTP client functions available.</end rant>
In this example we have defined two classes, one called ‘Tuxlabs’ and one called ‘Crawl’. Tuxlabs inherits a base class ‘object’ and the ‘Crawl’ class inherits ‘Tuxlabs’. If you’re wondering why Tuxlabs inherits from object see here. Our ‘Tuxlabs’ class has two methods a setter() & a getter() you will remember from the previous article on encapsulation. Now let’s focus on the real point in this example, the ‘Crawl’ class.
The ‘Crawl’ class as previously mention inherits the Tuxlabs class when it is defined or implemented ‘class Crawl(Tuxlabs):’. This means when an instance of Crawl initialized it will inherit all the attributes from the Tuxlabs class in addition to it’s own methods defined in the ‘Crawl’ class. The ‘Crawl’ class has only one method defined ‘download’ that takes a single argument, website.
Inside the download method in the crawl class we see ‘self.setter(website)’, but this is interesting, because we are using ‘self’ to call ‘setter()’, but self is actually referring to the ‘Crawl’ class and the ‘Crawl’ class only has download defined. So shouldn’t an error be thrown when we try to access ‘self.setter(website)’ inside the ‘Crawl’ class, when setter() lives in ‘Tuxlabs’ ?
No, and the answer is because self, which refers to our ‘Crawl’ class, which is actually inheriting from our ‘Tuxlabs’ class. So this actually works ! Now from within ‘Crawl’ we can access all methods and attributes in Tuxlabs or Crawl, nifty, handy and powerful are the words that come to mind. Before moving on to some even cooler features, let’s show the abbreviated output of this script (I can’t show the entire webpage output, yuck!).
➜ tuxlabs python example.py ... </body> </html> ➜ tuxlabs
Nothing magical there, but I did want to show you that this code works !
Now then, we should talk about __init__ & super().
__init__() if you don’t already know is the initial method called in a class. It is what other languages might refer to as a constructor. When you create an instance object of a class the __init__() method within that class is called and whatever code is in it is executed. Init can be good for setting up default/static values used within the scope of the class and other methods. A typical example of an init class might look like this.
class Foundation(object): def __init__(self, sqlCreds): self.sqlHost = sqlCreds['host'] self.sqlUser = sqlCreds['user'] self.sqlPass = sqlCreds['pass'] self.sqlDb = sqlCreds['db'] if 'port' in sqlCreds: self.sqlPort = int(sqlCreds['port']) else: self.sqlPort = 3306 self.sqlQuery = ''
Here we are defining a class ‘Foundation’ and an init constructor that accepts a dictionary as an argument containing SQL Credentials. Then within the init constructor we define several attributes who’s values are populated from that dictionary. We also made ‘port’ an optional argument with a default port of ‘3306’ the MySQL default port. This is a very useful start to a class, and it is my actual code from a project. From here we would build on top of this class many other methods like SQL queries we frequently used and other data processing techniques we frequently need like parsing JSON or XML.
Now let’s assume for a moment, this is a larger scale project. One that requires many classes. Classes that are inheriting from each other. Because the ‘Foundation’ class above is so generic, it can be used over and over again in other classes. However, those other classes will have slightly different purposes. In the case of databases, perhaps each class represents a different database. This makes sense, but we would still want to make use of the lovely constructor code inside of ‘Foundation’, thanks to it’s well written generic-ness. Here is an example of how we would do that, using something called super().
class Foundation(object): def __init__(self, sqlCreds): self.sqlHost = sqlCreds['host'] self.sqlUser = sqlCreds['user'] self.sqlPass = sqlCreds['pass'] self.sqlDb = sqlCreds['db'] if 'port' in sqlCreds: self.sqlPort = int(sqlCreds['port']) else: self.sqlPort = 3306 self.sqlQuery = '' ... class Cloud(Foundation): def __init__(self, sqlCredsCloud): super(Cloud, self).__init__(sqlCredsCloud) ...
We have our original class ‘Foundation’, and a new child class ‘Cloud’ that inherits ‘Foundation’. In addition ‘Cloud’ is defining it’s own __init__ constructor and accepting a dictionary for SQL credentials ‘sqlCredsCloud’. Finally, we call super() and pass our ‘Cloud’ class to the inherited class ‘Foundation’ (in this case self) and pass our SQL Creds to the ‘Foundation’ class constructor.
Whew.. I think I did an ok job explaining that, but you may need to read it back a couple of times and re-visit after trying to use this concept. Let’s take a look at how we would use these classes.
import sys sys.path.append('/Users/jriedel/PycharmProjects') from mymodules import foundation def setup_cloud_db(sqlCredsCloud): Cloud = foundation.Cloud(sqlCredsCloud) CloudCursor = Cloud.SqlConnect() return (CloudCursor, Cloud) if __name__ == '__main__': sqlCredsCloud = {'host': 'localhost', 'user': 'root', 'pass': '', 'db': 'cloud'} (CloudCursor, Cloud) = setup_cloud_db(sqlCredsCloud) Cloud.TruncateTable('cloud_machine_counts')
First we modify our Python path to locate the foundation module and import it. Then we create a subroutine for setting up our connection to the database and creating our instance object of our Cloud class. Notice we are passing sqlCredsCloud to the Cloud class and remember internally this is passing sqlCreds to the Foundation class constructor. Finally, in the main routine of the program, after we setup the credentials and after we setup our database connection, we call ‘TruncateTable’, which is a method within the Cloud class that I have not shown in our code above, but you get the idea. Well I think that is about all I can muster, you now know what I know about inheritance.
For more information on super() click here
See ya next time !
Jason Riedel
Object-Oriented Programming With Python : Inheritance (2/3) Read More »