Facebook SDK

Tuesday, April 1, 2014

Beginning Python Tutorial (Part 12)

Getting Classier

The last time we met I told you some of the basics about using Python classes and object-oriented programming. Today we will delve more into classes and see how they make programming life better.


First off, classes allow you to modify a program without really making changes to it. To elaborate, by subclassing a class, you can change the behavior of the program by simply adding new components to it rather than rewriting the existing components.
As we’ve seen, an instance of a class inherits the attributes of that class. However, classes can also inherit attributes from other classes. Hence, a subclass inherits from a superclass allowing you to make a generic superclass that is specialized via subclasses. The subclasses can override the logic in a superclass, allowing you to change the behavior of your classes without changing the superclass at all.
Let’s make a simple example. First make a class:
Generic Code Example:
>>>class FirstClass:           #define the superclass
… def setdata(self, value):    #define methods
…  self.data = value    #’self’ refers to an instance
… def display(self):  
…  print self.data
Then we make a subclass:
Generic Code Example:
>>>class SecondClass(FirstClass): #inherits from FirstClass
… def display(self):  #redefines ‘display’
…  print “Current value = ‘%s’” % self.data
As you can see, SecondClass “overwrites” the display method. When a FirstClass instance is created, all of its actions will be taken from the methods defined in FirstClass. When a SecondClass instance is created, it will use the inherited setdata method from FirstClass but the display method will be the one from SecondClass.
To make this easier to understand, here are some examples in practice.
Generic Code Example:
>>>x=FirstClass() #instance of FirstClass
>>>y=SecondClass() #instance of SecondClass
>>>x.setdata(“The boy called Brian.”)
The boy called Brian.
Current value = ‘42’
Both instances (x and y) use the same setdata method from FirstClass; x uses it because it’s an instance of FirstClass while y uses it because SecondClass inherits setdata from FirstClass. However, when the display method is called, x uses the definition from FirstClass but y uses the definition from SecondClass, where display is overridden.
Because changes to program logic can be made via subclasses, the use of classes generally supports code reuse and extension better than traditional functions do. Functions have to be rewritten to change how they work whereas classes can just be subclassed to redefine methods.
On a final note, you can use multiple inheritance (adding more than one superclass within the parenthesis) if you need a class that belongs to different groups. In theory this is good because it should cut down on extra work. For example, a person could be a chef, a musician, a store owner, and a programmer; the person could inherit the properties from all of those roles. But in reality it can be a real pain to manage the multiple inheritance sets. You have to ask yourself, “Is it really necessary that this class inherit from all of these others?”; more often than not the answer is, “No”.
Using multiple inheritance is considered an “advanced technique” and therefore I won’t discuss it. Actually, I don’t use it; if I encounter a situation where I could use it, I will try and rethink the program’s structure to avoid using it. It’s kind of like normalizing databases; you keep breaking it down until it’s as simple as you can get it. If you still need multiple inheritance, then I recommend getting a book

Operator Overloads

Operator overloading simply means that objects that you create from classes can respond to actions (operations) that are already defined within Python, such as addition, slicing, printing, etc. Even though these actions can be implemented via class methods, using overloading ties the behavior closer to Python’s object model and the object interfaces are more consistent to Python’s built-in objects, hence overloading is easier to learn and use.
User-made classes can override nearly all of Python’s built-in operation methods. These methods are identified by having two underlines before and after the method name, like this: __add__. These methods are automatically called when Python evaluates operators; if a user class overloads the __add__ method, then when an expression has “+” in it, the user’s method will be used instead of Python’s built-in method.
Using an example from Learning Python, here is how operator overloading would work in practice:
Generic Code Example:
>>>class ThirdClass(SecondClass): #is-a SecondClass
… def __init__(self, value): #on “ThirdClass(value)”
…  self.data = value
… def __add__(self, other): # on “self + other”
…  return ThirdClass(self.data + other)
… def __mul__(self, other): #on “self * other”
…  self.data = self.data * other
>>>a = ThirdClass(“abc”) #new __init__ called
>>>a.display()   #inherited method
Current value = ‘abc’
>>>b = a + “xyz”  #new __add__ called: makes a new instance
Current value = ‘abcxyz’
>>>a*3   #new __mul__ called: changes instance in-place
Current value = ‘abcabcabc’
ThirdClass is technically a subclass of SecondClass but it doesn’t override any of SecondClass’ methods. If you wanted, you could put the methods from ThirdClass in SecondClass and go from there. However, creating a new subclass allows you flexibility in your program.
When a new instance of ThirdClass is made, the __init__ method takes the instance-creation argument and assigns it to self.data.ThirdClass also overrides the “+” and “*” operators; when one of these is encountered in an expression, the instance object on the left of the operator is passed to the self argument and the object on the right is passed to other. These methods are different from the normal way Python deals with “+” and “*” but they only apply to instances of ThirdClass. Instances of other classes still use the built-in Python methods.
One final thing to mention about operator overloading is that you can make your custom methods do whatever you want. However, common practice is to follow the structure of the built-in methods. That is, if a built-in method creates a new object when called, your overriding method should too. This reduces confusion when other people are using your code. Regarding the example above, the built-in method for resolving “*” expressions creates a new object (just like how the “+” method does), therefore the overriding method we created should probably create a new object too, rather than changing the value in place as it currently does. You’re not obligated to “follow the rules” but it does make life easier when things work as expected.

Class Methods

Instance methods (which is what we’ve been using so far) and class methods are the two ways to call Python methods. As a matter of fact, instance methods are automatically converted into class methods by Python.
Here’s what I’m talking about. Say you have a class:
Generic Code Example:
class PrintClass:
 def printMethod(self, input):
  print input
Now we’ll call the class’ method using the normal instance method and the “new” class method:
Generic Code Example:
>>>x = PrintClass()
>>>x.printMethod(“Try spam!”) #instance method
Try spam!
>>>PrintClass.printMethod(x, “Buy more spam!”) #class method
Buy more spam!
So, what is the benefit of using class methods? Well, when using inheritance you can extend, rather than replace, inherited behavior by calling a method via the class rather than the instance.
Here’s a generic example:
Generic Code Example:
>>>class Super:
… def method(self):
…  print “now in Super.method”
>>>class Subclass(Super):
… def method(self):    #override method
…  print “starting Subclass.method”    #new actions
…  Super.method(self)    #default action
…  print “ending Subclass.method”
>>>x = Super() #make a Super instance
>>>x.method() #run Super.method
now in Super.method
>>>x = Subclass() #make a Subclass instance
>>>x.method()  #run Subclass.method which calls Super.method
starting Subclass.method
now in Super.method
ending Subclass.method
Using class methods this way, you can have a subclass extend the default method actions by having specialized subclass actions yet still call the original default behavior via the superclass. Personally, I haven’t used this yet but it is nice to know that it’s available if needed.

Have you seen my class?

There is more to classes than I have covered here but I think I’ve covered most of the basics. Hopefully you have enough knowledge to use them; the more you work with them the easier they are to figure out. I may have mentioned it before, but it took me almost six months to get my head around using classes. Objects were a new area for me and I couldn’t figure out how everything worked. It didn’t help that my first exposure to them was Java and C++; my two textbooks just jumped right into using objects and classes without explaining the how's and why's of them. I hope I did better explaining them than my text books did.

There are several “gotchas” when using classes, such as learning the difference between “is-a” and “has-a” relationships, but most of them are pretty obvious, especially when you get error messages. If you really get stumped, don’t be afraid to ask questions. Remember, we were all beginners once and so many of us have encountered the same problem before.

No comments: