I'm Learning Python part 9

صورة mpcabd
نشره mpcabd في الجمعة, 20/03/2009 - 1:53ص
I'm Learning Python part 9

Classes In Python

As I told you before, Python is a powerful programming language, and powerful it won't be without Object Oriented design.
Python supports both Pure Object Oriented Programming (Every thing must be in a class) and Structural Programming (You can type code everywhere).
It supports also other programming paradigms like Functional Programming. I won't write about OOP and its uses, why we should use it, I'll just give you the keys to use OOP in Python.

Defining a Class

Classes in Python are defined like this:

class CLASS_NAME (BASE0,BASE1,BASE2):
"""OPTIONAL DOC STRING"""
#Class Code
.
.
.

Yes, I know what you're thinking of, Python supports multiple inheritance.
In Python 3.0 you must at least inherit the object class (or any other class that inherits object), this is called new-style-classes, and that's because before Python 3.0 you could make a classic class that doesn't inherit any class, not even object.
Because of Python's dynamic data typing you don't need interfaces in Python, so there is no implicit syntax for declaring interfaces.

Sample

Let's create a sample class, I always implement the stack when I want to show OOP examples, I don't know why but that's what I'm gonna do now:
I always start implementing stack by implementing its unit type, a class called StackItem.
StackItem is a stack unit that has two properties, its value, and a reference to its next item in the stack, so it will be defined:

class StackItem(object):
def __init__(self, value):
self.Value = value
self.Next = None

@property
def Value(self):
return self.__value
@Value.setter
def Value(self, value):
self.__value = value

@property
def Next(self):
return self.__next
@Next.setter
def Next(self, value):
self.__next = value

Let's describe the whole scene of code:

  1. StackItem inherits object, it is a way of saying "Everything is an object" Smile
  2. __init__ is the constructor of StackItem, it takes two arguments, the instance of StackItem to construct and the value of it, it sets the given value to the Value property of the given StackItem instance. And it sets the Next property of the given StackItem instance to None (Which is the null pointer).
  3. @property is called function decorator, a decorator is a function which takes a function as a parameter and returns a function too. (Weird but handy).
    The decorator works as a transformer that transforms the function from one state to another. @property decorator creates a property from the given function, a property is like a variable, you store values in it for further reading/writing, but properties have a special function called a getter that returns the hidden value, and a special function called a setter that sets a given value to the hidden variable, the benefits you get from using properties instead of variables is that you can do some check before assigning the value to the variable, you can make sure that the give value is a positive number for instance.
    @property created a property called Value inside StackItem class, and it bounded its value to a private variable called __value.
    You'd ask how come __value is a private variable?
    The answer is that in Python there is no access modifiers but Python annotates that any class variable with two leading underscores at least and one trailing underscore at most will be a private class variable, so other classes can't see it, so it is a way of creating private variables.
  4. @Value.setter is another decorator which binds the setting of the Value property tho the given function.
  5. Next is like Value, a property.
    A property can be used later by calling its name only, just like any variable: e.g.:

     

    s.Value = 5
    #This will call the function with
    #@Value.setter decorator,
    #passes s as the first argument,
    #and 5 as the second one.

    print(s.Value)
    #This will call the function with
    #@property decorator,
    #passes s as the first argument.

  6. self is passed to each function in the class, it refers to the instance of the class that the function was called from (Like this in C++, C#, Java and Me in Visual Basic).
    When not passing self to the function you're declaring that this function doesn't need an instance of the class to be called, in other words, you're declaring this as a static function, but you must tell the compiler that you did it on purpose so you should add @staticmethod decorator to the function, e.g.:

     

    class C(object):
    def NonStatic(self):
    print ("NonStatic" )
    @staticmethod
    def Static():
    print("Static" )

    cObj = C()
    cObj.NonStatic() #Will print NonStatic
    #(self = cObj)

    cObj.Static() #Will print Static
    C.Static() #Will also print Static
    C.NonStatic() #Would give an error because
    #NonStatic expects 1 argument
    #and was called using none.

The other part must be easy for you after understanding the first one, now we'll implement the Stack class:

class Stack(object):
@property
def Head(self):
return self.__head

@property
def Count(self):
return self.__count

def __init__(self):
self.__head = None
self.__count = 0

def Push(self, value):
if (self.__head is None):
self.__head = StackItem(value)
else:
temp = StackItem(value)
temp.Next = self.__head
self.__head = temp
self.__count += 1

def Pop(self):
if (self.__head is None):
raise Exception("Empty Stack" )
else:
temp = self.__head
self.__head = self.__head.Next
self.__count -= 1
return temp.Value

def PopAll(self):
while (self.__count > 0):
yield self.Pop()

Notes:

  1. Stack has two read-only properties (They have no setters), Head (a reference to its top-most item) and Count (the count of items in the Stack).
  2. The constructor sets the Count to 0 and the Head to None.
  3. Push takes two arguments, the stack to push into, and the value to be pushed.
  4. Pop takes one argument, the stack to pop out from, and returns the popped value. If the stack is empty Pop method will raise (throw) a new Exception with a message that says "Empty Stack".
  5. PopAll is called a Generator, it generates values from a data structure, it can be used with for loops. e.g.:
    for value in s.PopAll():
    print(value)

    yield is a keyword in Python that yields Very Happy a given value to the caller. PopAll method works this way: The first time the caller calls it it would return the first value and stop. Each next time it will continue from where it stopped.

Now we can use our stack freely Smile

S = Stack()

map(S.Push, range(1, 10))

for value in S.PopAll():
print value

Simple code, right?
Notes:
  1. S = Stack() initializes a new Stack and put its reference in S.
  2. map is a function that takes a function as first argument and a list as second argument, and it calls the given function on each value from the give list and returns a tuple with the values of calling the function, in our case there is no return because Push returns no value. range is a function that takes two numbers and returns a list of numbers between the first one inclusively and the second one exclusively. e.g.:
    print(rang(1, 10)) #prints from 1 to 9

    range can also take three numbers, the third would be the step between each two numbers. e.g.:

    print(range(1, 10, 2)) #prints 1, 3, 5, 7, 9

    So map(S.Push, range(1, 10)) will fill the stack with values from 1 to 9.

  3. I used the PopAll method which is a generator to iterate over the StackItems and print them.

Overriding and Overloading

In OOP we use Overriding for a method in the sub-class that overrides a method in the base-class, and Overloading for two methods with the same name but differ in signature (number of arguments, types of arguments, order of arguments).
Usually to override a method in the sub-class the method must be declared virtual in the base-class, but in Python all methods are implicitly virtual.
To override a method you simply have to define it in the sub-class. e.g:

class A(object):
def M(self):
print (self.X)
def __init__(self):
self.X = 5
class B(A):
def M(self):
print (self.X + 1)

b = B()
b.M() #Will print 6

Sometimes you need to extend the base method instead of overriding it completely, you can call the base method by using BaseClassName.MethodName(self, arguments).
e.g.:

class C(B):
def M(self):
B.M(self)
print ("This is C" )
c = C()
c.M() #Will print 6 This is C

While overriding is a piece of cake, overloading is not supported by Python, you have to pick different names for different overloads from your method, that is because Python associates each function to a variable, so when overloading the variable will have the last function only.
A def statement is roughly executed like this:
  1. Compile the body of the function.
  2. Build a function object __f
  3. Assign it: FunctionName = __f

Bottom Line:

Python supports Pure Object Oriented strongly, You can write pure Object Oriented programs using Python, so why don't you start writing your programs now?
Cheers.