Saturday 21 May 2016

Python Object Programming Tips

----------------------------------------
Object Programming in python
----------------------------------------
Every thing in python is objects.

Class is collection of objects. But in python class is also an object of type.
Class contains data and methods.

Generally class contains constructor, methods, data. In python every method
of class must have first parameter as self.

Self -> It refers to object. Self is nothing but place holder of an object.
----------------------------------------
Constructor method in a class:
----------------------------------------
def __init__(self)  
------------------------
Types of variables
------------------------
There are two kinds of variables used in class :
> Instance variables (or object variables):
------------------------------------------------------
   Each object will have own copy of variable and the value is different across objects.

> class variables:
    --------------------
The same variable is shared across all objects. The value is common for all the objects.

How to create simple class?
------------------------------------
class Firstclass(object):

    # Constructor
    def __init__(self):
      print "Constructor is called"

if __name__ =="__main__":

   # Create the object
   # Object is created and constructor is called
   obj = Firstclass()

----------------------------------------------------------------
How to write simple class with Instance Variable?
----------------------------------------------------------------
class Firstclass(object):

    def __init__(self, x):
       self.instance_var = x

    def printCounter(self):
       print self.instance_var

if __name__ =="__main__":

   # Object 1 Created
   object1 = Firstclass(30)

   object1.printCounter()

   # Object 2 Created
   object2 = Firstclass(40)
   object2.printCounter()
-------------------------------------------------------------------
Simple Class with class variable, instance_variable
-------------------------------------------------------------------
class Firstclass(object):

    # class variable
    shared_variable = 0

    def __init__(self, x):
       # instance variable
       self.instance_var = x
       Firstclass.shared_variable += 1

    def printCounter(self):
       print "Class variable: %d" % Firstclass.shared_variable
       print "Instance variable: %d" % self.instance_var

if __name__ =="__main__":

   # Object 1 Created
   object1 = Firstclass(30)

   object1.printCounter()

   # Object 2 Created
   object2 = Firstclass(40)

Output:  The class variable is shared across objects.
---------
Class variable: 1
Instance variable: 30
Class variable: 2
Instance variable: 40

How to make private variables in class?
==============================
There are two ways to make variables as private in python.
Python using the name mangling to make the variables as private.

> Single underscore  ( _variable) ->weak private, it can accessed outside in the same file.
   But other module will not have access to these variables.

> Double underscore  ( __variable) ->strong private, it can't be accessed outside in the same file.
   Other module will not have access to these variables.

   There is a way to access these varibles. < objectName>._<class name>__<double underscore variable>

Sample Example
=============

class Myclass(object):

    # Weak private variable ( With single Underscore)
    _singleUnderscoreVariable=0

    # Strong private Variable (With Double Underscore)
    __doubleUnderscoreVariable=0


    def __init__(self):
       Myclass._singleUnderscoreVariable += 1
       Myclass.__doubleUnderscoreVariable +=1

    def printData(self):
       print "Single underscore: %d" % Myclass._singleUnderscoreVariable
       print "Instance variable: %d" % Myclass.__doubleUnderscoreVariable

if __name__ =="__main__":

   # Object 1 Created
   m_object = Myclass()

   # print data
   m_object.printData();

   # single underscore variable can be accessed
   # But it can't be imported
   print m_object._singleUnderscoreVariable
-------------------------------------------------------------
What is Static Method and Class Method in Python
---------------------------------------------------------------

-> Class method
functions defined inside class shall use the class rather than object.
But with class method, you could write instead:
There is a subtle performance benefit. If you refer to the full class name in a static method,
the Python interpreter has to first look up the local variables, not finding it there, and then look up the global variables.
With a class method, cls would be found in the local variables, so there is no need to look up the globals.

class Smoothie(object):

    YOGURT = 1
    STRAWBERRY = 2
    BANANA = 4
    MANGO = 8

    @staticmethod
    def blend(*mixes):
        return sum(mixes) / len(mixes)

    @classmethod
    def eternal_sunshine(cls):
        return cls.blend(cls.YOGURT, cls.STRAWBERRY, cls.BANANA)

    @classmethod
    def mango_lassi(cls):
         return cls.blend(cls.YOGURT, cls.MANGO)

if __name__ =="__main__":

   s = Smoothie()
   print s.eternal_sunshine()
   print s.mango_lassi()
   print Smoothie.mango_lassi()

---------------------
-> Static method
----------------------
Some times, functions defined inside class may not use the object. Such functions are called as Static Functions.
Static functions will not refer any class attributes.
static methods do not use per-instance object states, but they implement functionality grouped as part of the class.

class Smoothie(object):

    YOGURT = 1
    STRAWBERRY = 2
    BANANA = 4
    MANGO = 8

    @staticmethod
    def blend(*mixes):
        return sum(mixes) / len(mixes)

    @staticmethod
    def eternal_sunshine():
        return Smoothie.blend(
            Smoothie.YOGURT, Smoothie.STRAWBERRY,
            Smoothie.BANANA)

    @staticmethod
    def mango_lassi():
        return Smoothie.blend(
            Smoothie.YOGURT, Smoothie.MANGO)

if __name__ =="__main__":

   s = Smoothie()
   print s.eternal_sunshine()

   print Smoothie.eternal_sunshine()

Here you can call the static function using Object or with the class name.

========
Inheritance
========
Like C++, python also has inheritance. A class can inherit the properties of another class.

syntax:

class parent(object):  -> Parent class is also called as Super class
   pass

class child(parent):     -> Child class is also called as sub class
   pass
----------------------------------------
Simple Program of Inheritance
----------------------------------------
Implict Inheritance: implicit actions that happen when you define a function in the parent, but not in the child.

class Animal(object):

   def __init__(self):
      print " I am in Animal (Parent) Class"

   def printData(self):
      print " I am in Base Class Print"

#inherited
class Bear(Animal):

   def __init__(self):
      print " I am in Bear Class"

if __name__ =="__main__":

   # Create Lion Object
   animal_object = Animal()

   # Create Lion Object
   lion_object = Bear()

   animal_object.printData();
   lion_object.printData();

----------
Output:
-----------
I am in Animal (Parent) Class
 I am in Bear Class
 I am in Base Class Print
 I am in Base Class Print

------------------------
override Explicitly
-----------------------
The problem with having functions called implicitly is sometimes you want the child to behave differently.
 In this case you want to override the function in the child, effectively replacing the functionality.
To do this just define a function with the same name in Child. Here's an example:

class Animal(object):

   def __init__(self):
      print " I am in Animal (Parent) Class"

   def printData(self):
      print " I am in Base Class Print"

#inherited
class Bear(Animal):

   def __init__(self):
      print " I am in Bear Class"

   def printData(self):
      print " I am in Child Class"

if __name__ =="__main__":

   # Create Bear Object
   Bear_object = Bear()

   # print data  of bear
   Bear_object.printData();

   # Create Animal Object
   Animal_object = Animal()
----------
Output:
----------
 I am in Bear Class
 I am in Bear Print Class
 I am in Animal (Parent) Class
 I am in Base Class Print

--------------------------------------------------------------------
How to call the parent method inside the child class  (One kind of Inheritance)
--------------------------------------------------------------------
Super () -> Super key word can be used to call the parent class constructor or method.

super(<present class Name>, self). <method>

in Python 3: super().<method>

By Using the Super() keyword : Multiple inheritance is when you define a class that inherits from one or more classes, like this:

class derivered (parent1, parent2)
  pass

The common properties of two classes shall be inherited twice in the child class, and in python using super() keyword can be avoided.

To do this Python uses "method resolution order" (MRO) and an algorithm called C3 to get it straight.

Because the MRO is complex and a well-defined algorithm is used, Python can't leave it to you to get the MRO right.
Instead, Python gives you the super() function, which handles all of this for you in the places that you need the altering type of actions as I did in Child.altered.
With super() you don't have to worry about getting this right, and Python will find the right function for you.

-----------------------
Sample Program
-----------------------

class Animal(object):

   def __init__(self):
      print " I am in Animal (Parent) Class"

   def alterData(self):
      print "parent class alterted"

class Bear(Animal):

   def __init__(self):
      print " I am in Bear Class"
      # call the parent class constructor
      super(Bear, self ).__init__()

   def alterData(self):
      print "Child class alterted"
      # call the parent class constructor
      super(Bear, self ).alterData()

if __name__ =="__main__":

   Bear_object = Bear()
   Bear_object.alterData();

----------
Output :
---------
I am in Bear Class
 I am in Animal (Parent) Class
Child class alterted
parent class alterted
-----------------------------------------------------------------------
How to solve the Multiple inheritance Problem in Python:
-----------------------------------------------------------------------
Solution: Super keyword and it also used "method resolution order"

class A:
    def m(self):
        print("m of A called")

class B(A):
    def m(self):
        print("m of B called")
        super().m()
 
class C(A):
    def m(self):
        print("m of C called")
        super().m()

class D(B,C):
    def m(self):
        print("m of D called")
        super().m()

if __name__ =="__main__":
   obj = D()
   obj.m()
---------
Output:
m of D called
m of B called
m of C called
m of A called

The super function is often used when instances are initialized with the __init__ method:

class A:
    def __init__(self):
        print("A.__init__")

class B(A):
    def __init__(self):
        print("B.__init__")
        super().__init__()
 
class C(A):
    def __init__(self):
        print("C.__init__")
        super().__init__()


class D(B,C):
    def __init__(self):
        print("D.__init__")
        super().__init__()

if __name__ =="__main__":
   obj = D()
   obj.m()

   print obj.mro
-----------
Output:
------------
it uses the so-called method resolution order(MRO). It is based on the "C3 superclass linearisation" algorithm
  >>> d = D()
D.__init__
B.__init__
C.__init__
A.__init__

>>> c = C()
C.__init__
A.__init__
>>> b = B()
B.__init__
A.__init__
>>> a = A()
A.__init__

------------------
Composition:
------------------
Having some other class inside another class can be termed as composition. It C++ terms, it owns other class instance.
If the main class object destroys, the other object will be destroyed.

class Other(object):

    def override(self):
        print "OTHER override()"

    def implicit(self):
        print "OTHER implicit()"

    def altered(self):
        print "OTHER altered()"

class Child(object):

    def __init__(self):
        #other Class instance is created.
        self.other = Other()

    def implicit(self):
        self.other.implicit()

    def override(self):
        print "CHILD override()"

    def altered(self):
        print "CHILD, BEFORE OTHER altered()"
        self.other.altered()
        print "CHILD, AFTER OTHER altered()"

son = Child()
son.implicit()
son.override()
son.altered()