In [ ]:
# https://github.com/tuxfux-hlp/Python-examples/blob/master/opps/Good_links.txt

In [ ]:
# programming
# functional programming.
# object oriented programming.

In [ ]:
# A P I E
# A - Abstraction - how do you define your class object.
# P - Polymorphism - Many forms
# I - Inheritance
# E - Encapsulation

In [ ]:
# All the below are examples of abstaraction.
# example 1:
# bankaccount  - class/classobj
# - balance
# + deposit()
# + withdraw()
# + Sintrest()

# vipin    - 100,000 -> instance/object
# madan    - 10,000
# harshita - 1000

In [ ]:
# example 2:
# villas ( blueprints) - class

# villa1 -> yellow -> instance/objects
# villa2 -> pink
# villa3 -> green

In [ ]:
# Example 3:
# automobiles
# car (generic term) - class
# - color
# - body
# - wheels
# + break
# + accleration
# + clutch

# car models
# bmw           - instance/object
# skoda
# maruti
# audi
# benz
# nano

In [1]:
# Polymorphism

print 2 + 3  # 5
print '2' + '3' # 23

# banks
# intrest ( poly - many , morphism - forms)
# home loan intrest
# education loan intrest
# saving loan intrest
# personal loan intrest
# ulip intrest.


5
23

In [ ]:
# Inheritance
# the features or advantages u get from your parents

# father( lefthanded,brownhair,blackeyes)
# mother( righthanded,blondhair,blueeyes)

# child is inheriting features from both mother and father.
# child( lefhanded,blackhair,blueeyes)

In [ ]:
# Encapsulation
# hiding of data. But we don't have it in python
# getting the people to use function rather than data.

# bankaccount
# balance
# deposit()
# withdraw()

# add money - i should not modify the balance account direclty, i should use the deposit() function.
# withdraw money - i should not modify balance account direclty , i should use the withdraw() function.
# we are more concerned with the functionality rather than the data.
# pendrive - you plugin and see data. reading and writing data into the pendrive.

In [3]:
# bankaccount for learning object oriented programming.

# basic functional programming.
balance = 0

def deposit(amount):
    global balance
    balance = balance + amount

def withdraw(amount):
    global balance
    balance = balance - amount
    
# Main
# ramanji
print "balance of ramanji before deposit- {}".format(balance)
print "depositing the amount into account - 1000"
deposit(1000)
print "withdraw the amount from the account - 300"
withdraw(300)
print "balance of ramanji after all transactions - {}".format(balance)

# Sunil
print "balance of sunil before deposit- {}".format(balance)


balance of ramanji before deposit- 0
depositing the amount into account - 1000
withdraw the amount from the account - 300
balance of ramanji after all transactions - 700
balance of sunil before deposit- 700

In [6]:
# dictionary

def account():
    return {'balance':0}

def deposit(account,amount):
    account['balance'] =  account['balance'] + amount

def withdraw(account,amount):
    account['balance'] =  account['balance'] - amount
    
# Main
ramji = account() # ramji = {balance:0} , ramji['balance']
print ramji
print "balance of ramji is {}".format(ramji['balance'])
print "depositing the amount into account - 1000"
deposit(ramji,1000)
print "withdraw then amount from the account - 300"
withdraw(ramji,300)
print "balance of ramanji after all transactions - {}".format(ramji['balance'])

sunil = account()
print sunil
print "balance of sunil is {}".format(sunil['balance'])


{'balance': 0}
balance of ramji is 0
depositing the amount into account - 1000
withdraw then amount from the account - 300
balance of ramanji after all transactions - 700
{'balance': 0}
balance of sunil is 0

In [ ]:
# object oriented concept.

In [21]:
# self represents the object itself.

# Abstraction of a class account.
# class is a keyword to begin your object oriented programming.
# bal -> variables/sdata
# deposit,withdraw,my_balance -> methods of class.
# Madhav,Madan -> objects/instances
# self -> represents the objects itself.

# class account(object):
class account:                                # class
    bal = 0
    def deposit(self,amount):                 # methods
        self.bal = self.bal + amount
    def withdraw(self,amount):                # methods
        self.bal = self.bal - amount
    def my_balance(self):                     # methods
        print "my balance is {}".format(self.bal)
        
print type(account)   # 'classobj'/class
print type(account()) #  'instance'/object

# Madhav - instance/object
Madhav=account()
print dir(Madhav)
Madhav.deposit(1000)


# Madan - instance/object
Madan=account()
print dir(Madan)
Madan.deposit(2000)


<type 'classobj'>
<type 'instance'>
['__doc__', '__module__', 'bal', 'deposit', 'my_balance', 'withdraw']
['__doc__', '__module__', 'bal', 'deposit', 'my_balance', 'withdraw']

In [15]:
Madhav.my_balance()


---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-15-15812316265d> in <module>()
----> 1 Madhav.my_balance()

<ipython-input-14-dcc5e40c1a80> in my_balance(self)
      6         self.bal = self.bal - amount
      7     def my_balance(self):
----> 8         print "my balance is {}".format(bal)
      9 
     10 print type(account)   # 'classobj'/class

NameError: global name 'bal' is not defined

In [19]:
Madhav.my_balance()


my balance is 1000

In [22]:
Madan.my_balance()


my balance is 2000

In [23]:
# Methods
# important method - constructor. __init__
# The first function to get into action, when you create an object is called constructor.

# class account(object):
class newaccount:# class
    def __init__(self,minbal):                # Method/constructor
        self.bal = minbal
    def deposit(self,amount):                 # methods
        self.bal = self.bal + amount
    def withdraw(self,amount):                # methods
        self.bal = self.bal - amount
    def my_balance(self):                     # methods
        print "my balance is {}".format(self.bal)

In [27]:
# Anil
# salaried

# constructor is the first function/method which gets called when you try to access the Anil account.
Anil = newaccount(minbal=0)
print dir(Anil)
Anil.my_balance()

# Harshita
# student
Harshita = newaccount(minbal=10000)
print dir(Harshita)
Harshita.my_balance()


['__doc__', '__init__', '__module__', 'bal', 'deposit', 'my_balance', 'withdraw']
my balance is 0
['__doc__', '__init__', '__module__', 'bal', 'deposit', 'my_balance', 'withdraw']
my balance is 10000

In [31]:
# Inheritance
# the features or advantages u get from your parents

# father( lefthanded,brownhair,blackeyes)
# mother( righthanded,blondhair,blueeyes)

# child is inheriting features from both mother and father.
# child( lefhanded,blackhair,blueeyes)

class Father:
    hand="lefty"
    hair="brown"
class Mother:
    hand="right"
    hair="blond"
    eyes="blue"
    
class child(Father,Mother):
    hair="black"
    
# Main
Kumar = child()
print dir(Kumar)
print Kumar.hair
print Kumar.hand
print Kumar.eyes


['__doc__', '__module__', 'eyes', 'hair', 'hand']
black
lefty
blue

In [36]:
raise santosh


---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-36-460ec4d018fb> in <module>()
----> 1 raise santosh

NameError: name 'santosh' is not defined

In [37]:
# santosh is inherting the features of the Exception class.
class santosh(Exception):
    pass

raise santosh,"I am back"


---------------------------------------------------------------------------
santosh                                   Traceback (most recent call last)
<ipython-input-37-5a7018151859> in <module>()
      2     pass
      3 
----> 4 raise santosh,"I am back"

santosh: I am back

In [33]:
# example
# Movies
# A,U,A/U 18yrs and above. 

age = input("please enter your age:")

class InvalidAgeError(Exception):
    def __init__(age):
        self.age = age

def validate_age(age):
    if age < 18:
        raise InvalidAgeError(age)
    else:
        return "you are old enough to see the movie- {}".format(age)
    
    
try:
    validate_age(age)
except Exception as e:
    print "you are not allowed to watch the movie {}".format(e.age)
else:
    print validate_age(age)


please enter your age:23
you are old enough to see the movie- 23

In [35]:
# example
# Movies
# A,U,A/U 18yrs and above. 

age = input("please enter your age:")

# i created a custome exception called InvalidAgeError
# parent exception is Exception.
# child is InvalidAgeError

class InvalidAgeError(Exception):
    def __init__(self,age):
        self.age = age

def validate_age(age):
    if age < 18:
        raise InvalidAgeError(age)
    else:
        return "you are old enough to see the movie- {}".format(age)
    
    
try:
    validate_age(age)
except Exception as e:
    print "you are not allowed to watch the movie {}".format(e.age)
else:
    print validate_age(age)


please enter your age:12
you are not allowed to watch the movie 12

In [45]:
# inheritance example on the bank
# ICICI account
# for salaried people - you can keep a zero balance or you can get a overdraft.
# for non-salaried people - we need to maintain a minimum balance.

# class account(object):
class account:                                # class
    def __init__(self):                       # special method - constructor
        self.bal = 0
    def deposit(self,amount):                 # methods
        self.bal = self.bal + amount
    def withdraw(self,amount):                # methods
        self.bal = self.bal - amount
    def my_balance(self):                     # methods
        print "my balance is {}".format(self.bal)
    
# you can inherit data and method from parents class but , you need to import the constructor(__init__) explicity.
class minaccount(account):
    def __init__(self):
        account.__init__(self)
    def withdraw(self,amount):
        if self.bal - amount < 1000:
            return "buddy!!! you need to call your daddy"
        else:
            account.withdraw(self,amount)
    
# inheritance - minaccount is child of account class.
# Polymorphism - so we have withdraw() both in account and also minaccount.
# both function have different functionlities , its called as polymorphic
# There is an order of looking for functions.
# Harshita.my_balance() , first its looks into the minaccount class and later it looks into the account class
    
    
# kumar
kumar = account()
kumar.my_balance()
kumar.withdraw(1000)
kumar.my_balance()

# Harshita - you have to manage a min balance of 1000 rupees
Harshita = minaccount()
Harshita.my_balance()
Harshita.deposit(2000)
Harshita.my_balance()
Harshita.withdraw(1500)


my balance is 0
my balance is -1000
my balance is 0
my balance is 2000
Out[45]:
'buddy!!! you need to call your daddy'

In [46]:
Harshita.withdraw(800)

In [47]:
Harshita.my_balance()


my balance is 1200

In [48]:
# Polymorphism
# magic methods : https://github.com/RafeKettler/magicmethods
# https://github.com/RafeKettler/magicmethods/blob/master/magicmethods.pdf

print 3 + 2
print '3' + '2'


5
32

In [54]:
a = 3
b = 2
print type(a),type(b) # a,b are instances/objects of 'int' class
print dir(a)
print a + b
print a.__add__(b)


<type 'int'> <type 'int'>
['__abs__', '__add__', '__and__', '__class__', '__cmp__', '__coerce__', '__delattr__', '__div__', '__divmod__', '__doc__', '__float__', '__floordiv__', '__format__', '__getattribute__', '__getnewargs__', '__hash__', '__hex__', '__index__', '__init__', '__int__', '__invert__', '__long__', '__lshift__', '__mod__', '__mul__', '__neg__', '__new__', '__nonzero__', '__oct__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'imag', 'numerator', 'real']
5
5

In [56]:
c = '3'
b = '2'
print type(c),type(b) # c,b are instances/objects of 'str' class
print c.__add__(b)


<type 'str'> <type 'str'>
32

In [57]:
# Adding two Rational number
# 1/5 + 1/2 = 7/10 = (1 * 2 ) + (5 * 1) / 5 * 2 = 7/10
# http://anandology.com/python-practice-book/object_oriented_programming.html#special-class-methods

In [59]:
print 1/5 + 1/2
print 1/5.0 + 1/2.0


0
0.7

In [60]:
class RationalNumber:
    """
    Rational Numbers with support for arthmetic operations.

        >>> a = RationalNumber(1, 2)
        >>> b = RationalNumber(1, 3)
        >>> a + b
        5/6
        >>> a - b
        1/6
        >>> a * b
        1/6
        >>> a/b
        3/2
    """
    def __init__(self, numerator, denominator=1):   # constructor
        self.n = numerator
        self.d = denominator

    def __add__(self, other):
        if not isinstance(other, RationalNumber):
            other = RationalNumber(other)

        n = self.n * other.d + self.d * other.n
        d = self.d * other.d
        return RationalNumber(n, d)
    
    def __str__(self):
        return "%s/%s" % (self.n, self.d)

    __repr__ = __str__

In [62]:
a = RationalNumber(1,5)  # a.n = 1,a.d=5
b = RationalNumber(1,2)  # b.n = 1,b.d=2
print a + b # + is calling __add__ by default
            # a.__add__(b)


7/10

In [63]:
c = RationalNumber(1,5)  
d = 2
print c + d


11/5

In [64]:
e = 12
f = 13
print e + f


25

In [ ]:
# encapsulation
# http://radek.io/2011/07/21/private-protected-and-public-in-python/
# https://importantshock.wordpress.com/2006/11/03/adventures-in-pythonic-encapsulation/

# encapsulation is all about functionality rather than directly accessing the data.
# public,protected,private
# Python doesn’t have any mechanisms, that would effectively restrict you from accessing a 
# variable or calling a member method. All of this is a matter of culture and convention.

In [12]:
# Cup class containts only public variables.Everyone can access them.
class Cup:
    def __init__(self):       # methods
        self.content = None   # data
        self.color = None     # data
    def fill(self,drink):     # mothods
        self.content = drink
    def empty(self):  # methods
        self.content=None

In [14]:
RedCup = Cup()

In [3]:
print dir(RedCup)


['__doc__', '__init__', '__module__', 'color', 'content', 'empty', 'fill']

In [4]:
RedCup.content="cofee"
RedCup.color="Red"

In [6]:
print RedCup.content
print RedCup.color


cofee
Red

In [7]:
RedCup.fill("tea")

In [9]:
print RedCup.content


tea

In [15]:
RedCup.empty()
print RedCup.content


None

In [16]:
# Protected
# putting a _ before a variable literally makes it private.
# you should access the private variable only from within a class or subclass.
# using of a _<variable> literally changes nothing. Its just a convention to use the private variables only using method of the 
# class.

class Cup:
    def __init__(self):       # methods
        self._content = None   # Protected
        self.color = None     # data
    def fill(self,drink):     # mothods
        self._content = drink
    def empty(self):  # methods
        self._content=None

In [17]:
BlueCup = Cup()

In [18]:
print dir(BlueCup)


['__doc__', '__init__', '__module__', '_content', 'color', 'empty', 'fill']

In [19]:
#  _content is declared a private variable and should not be touched directly.

In [20]:
# what i expect others(collegus/programmes) to do.
BlueCup.color = "blue"

In [21]:
BlueCup.fill('boost')

In [22]:
BlueCup._content


Out[22]:
'boost'

In [23]:
BlueCup.empty()
print BlueCup._content


None

In [24]:
# fun part
BlueCup._content = "pepsi"

In [25]:
print BlueCup._content


pepsi

In [31]:
# Private
# A private variable can have a two underscores before the variable : __variable

class Cup:
    def __init__(self):       # methods
        self.__content = None   # Private
        self.color = None     # data
    def fill(self,drink):     # mothods
        self.__content = drink
    def empty(self):  # methods
        self.__content=None
    def contains(self):
        return self.__content

In [36]:
YellowCup = Cup()

In [37]:
YellowCup.fill("horlicks")

In [38]:
YellowCup.__content


---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-38-f890fea35274> in <module>()
----> 1 YellowCup.__content

AttributeError: Cup instance has no attribute '__content'

In [39]:
print YellowCup.contains()


horlicks

In [40]:
# Name Mangling
print dir(YellowCup)


['_Cup__content', '__doc__', '__init__', '__module__', 'color', 'contains', 'empty', 'fill']

In [42]:
# _Cup__content
print YellowCup._Cup__content  # Accessing a private variable in not so nice way.


horlicks

In [51]:
YellowCup._Cup__content = "bournvita"
print YellowCup._Cup__content


bournvita

In [52]:
print YellowCup.contains()


bournvita

In [ ]: