====== About Python III ======
===== Python is object oriented =====
==== Object oriented concepts ====
Object oriented languages are languages that let you create **self-governing entities**. A self-governing entity is one that takes responsibility for managing its own **state** through a set of predetermined **behaviors**. Such self-governing entities are called **objects**.
The concept of an object in computing comes directly from the concept of an "object" in the real world. An example of an object from the real world is a mobile telephone.
=== State ===
At any given instant, a mobile telephone has a particular state. //Is it on? What is the battery charge level? What phone numbers are stored in its address book? Does it have a connection to the service provider?// And so on. All these attributes collectively define the phone's state.
=== Behavior ===
A mobile phone also comes with a set of behaviors. //When I push a certain set of buttons in the right order, it uses its network connection to connect to another phone. When I push a certain button long enough, it turns off. When I push another button, the sound level increases.// And so on. These operations collectively define the phone's behaviors. We call the public-facing set of behaviors (i.e., the ones a user can engage) the phone's **interface**.
=== Protection and encapsulation ===
As a user, I can change the state of the telephone only by engaging one or more of its behaviors in the phone's interface. In other words, I can't open up the phone and apply some mystery physics to change the charge level of the battery. To change the state of the charge level, I have to engage the phone's charging behavior (plugging in the power adapter) or wait for the battery to discharge. I (as a user) don't know how these things happen, and if I am a typical user I don't really care. I only know what changes in state to expect from the "plug adapter in", "let phone idle", and "place a call" behaviors.
As another example, the only way I can add a contact to the phone's address book is by engaging the "enter new contact" behavior. The behavior takes care of all the details of getting the data to the right place, etc. As a user, I don't need to know exactly how it does it. I only need to know what to expect from the behavior. In fact, even if I wanted to directly add new data to the address book (i.e., bypassing the behavior and fiddling with the phone's memory myself), I couldn't--at least not without a whole lot of pain and bother. The phone's insides are normally protected against public fiddling by [[http://www.ifixit.com/blog/blog/2011/01/20/apples-diabolical-plan-to-screw-your-iphone/|screws]]. And even if I manage to open it up, I would still need to know where the memory is, what its electrical parameters are, and a whole bunch of other hackery. No thanks. It's better to just use the public-facing set of behaviors--its interface--than get a PhD in phone design.
Keeping the user out of bits that the user should not be allowed to access is called **protection**. In general computing, the "not caring about how it works--I only need to know what it does" is part of **encapsulation** (literally, "to place in a capsule"). With objects, encapsulation also includes making sure that the public cannot directly reach members that it has no business directly reaching (i.e., protection).
=== A mobile phone is an object ===
Since our mobile phone takes responsibility for managing its own state using a set of predetermined behaviors, it is a self-governing entity. And so it is an object.
=== Class-based object orientation ===
I have three friends who have mobile phones. Jimmy has a [[http://europe.nokia.com/find-products/devices/nokia-n8|Nokia N8]]. Angie and Sally each use an [[http://www.youtube.com/watch?v=oHg5SJYRHA0|Apple iPhone 4]]. There is a factory ([[http://conversations.nokia.com/2010/09/30/nokia-n8-is-shipping-photos/
|probably in China]]) that is making Nokia N8s by the hundreds. There is also a factory ([[http://www.guardian.co.uk/world/2010/may/27/foxconn-suicide-tenth-iphone-china|definitely in China]]) making iPhones by the thousand millions.
Both factories make their phones based on master plans. The master plan for Apple's iPhone 4 defines what an iPhone 4 is. From one plan, the iPhone factory can make many, many phones. In computing terminology, we would call that master plan a //class//. A class contains all the specifications needed to make a particular kind of object. Each iPhone 4 begins its life identical to every other freshly-made iPhone 4 because they were made using the same master plan. So, the iPhone that Angie pulled out the box that her dad gave her on Christmas was identical to the one that Sally pulled out of her box on her birthday. But as Angie and Sally begin using their phones, the state of each phone changes. The behaviors designed into the phones don't (and won't) change, but the state does.
In computing, we call objects that have been created from classes **instances**. Thus Jimmy's phone is an instance of a Nokia N8, Angie's phone is an instance of an iPhone 4, and Sally's is another instance of an iPhone 4.
=== Prototype-based object orientation ===
There is another kind of object orientation called **prototype-based object orientation** or **prototype-based programming**. This is the kind of object-orientation used in JavaScript and some other languages.((C++, Java, C#, and PHP support only class-based object orientation; JavaScript and Ruby are inherently prototype-based; Objective-C is class-based but lets you do prototypal-based programming.)) I will leave it you to get your Google on and learn more about prototype-based object orientation.
==== Python objects ====
Python is inherently class-based, but it's not hard to do prototypal-style programming as well. We will stick to class-based object orientation here.
We will next define a class in Python and instantiate and use some objects.
=== Defining classes in Python ===
We are going to write a class for implementing one of these:((Picture from: "Totty Clicker - Gadgets at Play.com (UK)." Play.com (UK): DVDs, Music CDs, MP3s, Video Games, Books, Electronics & Gadgets - Free Delivery. [[http://www.play.com/Gadgets/Gadgets/4-/11566684/Totty-Clicker/Product.html?ptsl=1&ob=Price&fb=0#]] (accessed January 25, 2011).
)) \\ {{tottycounter.jpg?234|}}
A good way to start building a model for an object or class is to start listing the public-facing behaviors (or the //interface//) you want the class to have. A pretty comprehensive list of the things you might do with a clicker-counter is:
* //click//: makes the count increase by one.
* //reset//: sets the count to zero.
Next we can think about what kinds of things we'll need to keep track of the state of a clicker-counter. In this case, it's pretty simple: we really only need an integer to store the count value.
So, a summary of what we need so far is:
* a //click// behavior
* a //reset// behavior
* an integer to store the count
In Python, object attributes (which together make up the state) are defined in special variables called **instance variables**. A class definition can include as many instance variables as it needs to store the state. In our case, we are getting off easy: the clicker-counter only needs one. Object operations (which make up behavior) are defined using **instance methods**. In Python a method is nothing more than a function defined inside a class. We call anything belonging to a class (e.g., instance variables and methods) a **member** of the class.
Class names in Python traditionally use [[http://www.c2.com/cgi/wiki?CamelCase|CamelCase]] with the first letter capitalized.
=== Constructors in Python ===
When objects are created (i.e., **instantiated**), you almost always need to do some initialization of the object. This is so common that most object-oriented languages implement the concept of a **constructors**: a method or method-like entity that is automatically invoked whenever you instantiate an object.
Constructors in Python are implemented just like regular methods, but they use the special name ''%%__init__%%'' (that's *two* underscores, "init", and two more underscores). All the member variables that the object will use should be created and initialized inside an ''%%__init__%%'' method. Not only is it good programming practice, but also if you try to access a member variable that has not yet been created, the Python interpreter will bark at you and your script will probably fail.
=== Our first Python class ===
Here's a Python definition for a ''ClickerCounter'' class:
class ClickerCounter():
def __init__(self): # constructor
self.count = 0 # create instance variable for storing the count state
def click(self): # method for clicking
self.count = self.count + 1
def reset(self): # method for resetting
self.count = 0
**Note**: What look like blank lines above are actually lines with four spaces in them. If you don't "indent" empty lines, then Python will not include the following line in the same block! If you copy/paste this code, you may have to add the spaces yourself.
The keyword ''self'' above is a special variable that means "this instance" or "yourself". So ''self.count'' refers to the instance variable ''count'' associated with the object and ''def click(self):'' means we are defining a method that operates on the object. Notice that //all instance methods have ''self'' as the first argument.//
Once you have a definition for a class, you can instantiate a objects:
my_clicker = ClickerCounter()
You can see what ''my_clicker'' is with ''type()'':
>>> type(my_clicker)
This tells us that ''my_clicker'' is an object of the ''ClickerCounter'' class, which is defined in the ''%%__main__%%'' scope.
We can also see it's value:
>>> print my_clicker
<__main__.ClickerCounter object at 0xb743ceec>
Note that the value of ''my_clicker'' is not 0 or 1 or 2 or whatever value ''my_clicker.count'' has. The value of ''my_clicker'' is actually the memory address where the ''my_clicker'' object is stored. 0 or 1 or 2 or whatever is the value of the ''my_clicker.count'' instance variable.
Stuff like the above is useful for debugging. Most of the time, once you instantiate objects, you start to use them:
a = ClickerCounter() # make a clicker-counter
a.click() # count should now be 1
a.click() # count should now be 2
a.click() # count should now be 3
print a.count # ... is it?
b = ClickerCounter() # make another clicker
b.click() # count should now be 1
b.click() # count should now be 2
b.reset() # count should now be 0
print b.count # ... is it?
print a.count # this counter-clicker should still be 3
=== Protecting members ===
The ''ClickerCounter'' class we created above works fairly well, but it has a problem:
a = ClickerCounter() # make a clicker-counter
a.click()
a.reset()
a.count = -666 # WTF?
print a.count
a.count = "I am in your object, eating your chickens." # WTF?
print a.count
The problem is that the ''count'' instance variable is exposed to the public. This is analogous to making a real-world clicker-counter that lets the user open up the device and set the display to any value s/he chooses. Not the best idea. Usually.
One solution to this problem is to adopt a coding practice((A **coding practice** is a rule that you as a programmer or programming team agree on regarding how you should write your code. Coding practices are not syntax rules imposed by the language. For example, a common Python coding practice is to place a single underscore in front of instance members that should not be accessed directly.)): //**"Do not ever access an instance variable directly."**//
a.count = 3 # don't do this
print a.count # don't do this either!
So, if our coding practice says we are not allowed to access instance variables directly, how can you possibly use them? The answer is **accessors** and **mutators**.((Python also provides a certain amount of additional protection via [[http://docs.python.org/tutorial/classes.html#private-variables|name-mangling]]. But name mangling might not work entirely as you expect unless you carefully study how name-mangling works as well as Python's scoping rules.))
== Accessors ==
An **accessor** is a method that lets you read from (but not write to) instance variables. Typically, they are named ''get_{name-of-variable}(self)'' or ''get{name-of-variable}(self)''.
class ClickerCounter():
def __init__(self): # constructor
self.count = 0
def get_count(self): # accessor for count
return self.count
def click(self): # click the counter
self.count = self.count + 1
def reset(self): # reset the count
self.count = 0
== Mutators ==
A mutator is a method that lets you write to an instance variable. Typically, they are named ''set_{variable-name}(self,{new-value})'' or ''set{variable-name}(self,{new-value})''. A mutator should check that the new-value makes sense. For example, we //might// define a ''set_count'' mutator as follows:
class ClickerCounter():
def __init__(self): # constructor
self.count = 0
def set_count(self, count): # mutator for count
if isinstance(count, int) and count >= 0:
self.count = count
else:
print count, "is not an integer!"
def get_count(self): # accessor for count
return self.count
def click(self): # click the counter
self.count = self.count + 1
def reset(self): # reset the count
self.count = 0
Now we can:
a = ClickerCounter() # make a clicker-counter
a.click()
a.click()
a.click()
print a.get_count() # should be 3
a.reset()
print a.get_count() # should be 0
a.set_count(33)
print a.get_count() # should be 33
a.set_count("Evil...") # what happens?
print a.count # this works, but don't do it!
a.count = 99 # this works, but don't do it!
As long as we don't access instance variables directly but use accessors and mutators instead, we will have a proper, self-governing ''ClickerCounter''.
Note that without the ''set_count'' mutator, we still have a nice, usable class. If your class does not need a particular mutator or accessor, don't feel that you must add it.
== Properties ==
While the "Don't ever access instance variables directly" coding practice is an effecive way of encapsulating an object's state (with or without [[http://docs.python.org/tutorial/classes.html#private-variables|name-mangling]]), a much better approach is to use [[http://docs.python.org/library/functions.html#property|Python properties]]. We might look at that later.
Copyright © 2011 Mithat Konar. All rights reserved.