Pages

Tuesday, August 1, 2017

Python OOP fundamentals

Print:
print("%s is %s years old" % (name, age))
print('{0} is {1} years old'.format(name, age))

format in custom way:
print("First: {foo}, Second: {bar}".format(bar=22.42,foo=41))  

# reuse single. used more than once
print("More: {0:6.2f} or {0:6.3f}".format(5.1234))

# ignore multiple. Extra parameters ignored.

print("More: {1:6.2f} or {1:6.3f}".format(12.345, 5.1234))

Class name
1. private (_)
- begins with single underscore
def _copy(list):

- means used in present module
not available elsewhere
module “import” ignored
internal use only

2. Framework-reserved (_ _)
- Two or more
  must start and end with _
def __init__

pip = pip install packages
Pypi = python package index
PEP : Python enhancement proposal

def show():
  print("\t***************")
  print('\t* Hello World *')
  print('\t***************')

show()

or

banner:

def ShowStars(num):
  return "\t*" + ("*" * (num + 3))
  
def Show(message):
  xx = len(message)
  stars = ShowStars(xx)
  print(stars)
  print("\t* " + message + " *")
  print(stars)
  
Show("This is a MESSAGE")

Type change:
sNum = '20'
print(type(sNum))
iNum = int(sNum)
print('*'*iNum)

comparison operators:
__gt__()
__lt__()

import builtins
help(builtins)

isFun = True
isTough = False
if isFun or isTough:
  print("one out of 2 ain't bad")

if not isFun:
  print("Practices makes it fun")
  
if not (isFun and isTough):
  print("Learn C/C++")

sorting dict keys:

def sortLogic(ref):
  return len(ref)
  
data = {"First":"John", \
        "Last": "Doe",  \
        "Phone": "95496349",  \
        "Email": "test@hotmail.com"}

zkeys = list(data.keys())
print(zkeys)
zkeys.sort(key=sortLogic)
print(zkeys)

for zkey in zkeys:
  print("%10s:[%-20s]" % (zkey, data[zkey]))

sorted:
print(sorted(("Mr. Ed", "Mar Daisy", \
              "Mr. T", "Dr. Who"))) # tuple
print(sorted(["Mr. Ed", "Mar Daisy", \
              "Mr. T", "Dr. Who"])) # list
print(sorted({"Mr. Ed", "Mar Daisy", \
              "Mr. T", "Dr. Who"}))  # set
print(sorted({4:"Mr. Ed", 2:"Mar Daisy", \
              3:"Mr. T", 1:"Dr. Who"}))    # Dict Keys 


An object has two characteristics:
attributes
behavior
Let's take an example:
Parrot is an object,
name, age, color are attributes
singing, dancing are behavior
The concept of OOP in Python focuses on creating reusable code. This concept is also known as DRY (Don't Repeat Yourself).
In Python, the concept of OOP follows some basic principles:
Inheritance A process of using details from a new class without modifying existing class.
Encapsulation Hiding the private details of a class from other objects.

Polymorphism A concept of using common operation in different ways for different data input.


# class defines methods and data attributes of particular object
# The special __init__ method of class.Its job is to initialise or assign some values to its data attributes whenever we first create object.
# attributes are data we can assign values to and which can be used later.
# methods are used to perform operation on data attributes
# class attributes can be used outside class

--> A class is a blueprint for the object. From class, we construct instances. An instance is a specific object created from a particular class.
class Employee:
  def __init__(self):
    self.firstname = 'nawraj'
    self.lastname = 'Lekhak'

def main():
  print('This python programming is now starting')
    
main() 

when ever you define main it should be on level of class not within class because it is not class method and considered as main function.


### create class attributes
class Person:
  'Base class of a person'
  
  # class attributes
  first_name = 'Nawraj'
  last_name = 'lekhak'
  age = 30
  
def main():
  
  #print the title of the program
  
  print("\t *************Welcome to Employee Database ***********")
  print()
  
  # Print the class attributes values.
  print("First name is:", Person.first_name)
  print("Last name is:", Person.last_name)
  print("Age:", Person.age)
  print() ## to add additional line for seperation
  
# Call the main method to start the program
main()


Object

--> An object (instance) is an instantiation of a class. When class is defined, only the description for the object is defined. Therefore, no memory or storage is allocated


Methods

--> Methods are functions defined inside the body of a class. They are used to define the behaviours of an object
##create object based on Employee class
class Employee:
  'Base class of a Employee'
  
  # set default constructor
  ## instance attributes
  def __init__(self):
    self.first_name  = "Nawraj" # to reference data attributes
    self.last_name = "Lekhak"
    self.uid = '321'
    
def main():
  # create object based on Employee class
  # instantiate the Employee class
-->

  employee_01 = Employee() ## here employee_01 is obj of class Employee
  employee_02 = Employee()
  employee_03 = Employee()

##Obtain object attributes
class Employee:
  'Base class for our Employee'
  
  # set default constructor, instance attributes
  def __init__(self):
    self.first_name  = "Nawraj" # to reference data attributes
    self.last_name = "Lekhak"
    self.uid = '321'
   
 # instance method 
  def get_first_name(self):
    return self.first_name
    
  def get_last_name(self):
    return self.last_name
    
  def get_uid(self):
    return self.uid
    
def main():
  # create object based on Employee class
  employee_01 = Employee()
  
  ## Access and print the first last_name. Access class attributes
  print('First name:', employee_01.get_first_name())
  print('Last name:', employee_01.get_last_name())
  print('Uid:', employee_01.get_uid())
  
# call main method
main()

##Changing object attribute values
class Employees:
  'Base class for our Employees'
  
  # set default constructor when new objects are created
  def __init__(self):
    self.first_name  = "n/a" # to reference data attributes
    self.last_name = "n/a"
    self.uid = 'n/a'
    
def main():
  # create object based on Employees class
  employee_01 = Employees()
  print('First name:', employee_01.first_name)
  print('Last name:', employee_01.last_name)
  print('Uid:', employee_01.uid)
  print()
  
  # Let's modify this object attribute values
  employee_01.first_name = 'Nawraj'
  employee_01.last_name = 'Lekhak'
  employee_01.uid = '388921'
  
  # Print new assigned values
  print("New Values:")
  print('First name:', employee_01.first_name)
  print('Last name:', employee_01.last_name)
  print('Uid:', employee_01.uid)
  
# start out program
main()

##Acessing object methods
class Employees:
  'Base class for Employees'
   
  # set default constructor when new objects are created
  def __init__(self):
    self.first_name  = "Nawraj" 
    self.last_name = "Lekhak"
    self.uid = '102'
  
  ## Getter method
  def get_first_name(self):
    return self.first_name
    
  def get_last_name(self):
    return self.last_name
    
  def get_uid(self):
    return self.uid
  
  ## set method  
  def set_first_name(self, xfirstname):
    self.first_name = xfirstname
    
  def set_last_name(self, xlastname):
    self.last_name = xlastname
    
  def set_uid(self, xuid):
    self.uid = xuid 

def main():
  # create object based on Employees class
  employee = Employees()
  
  ## access getter methods
  print('First name:', employee.get_first_name())
  print('Last name:', employee.get_last_name())
  print('Uid:', employee.get_uid())
  print()
  
  # Modify object attributes Values
  employee.set_first_name('John')
  employee.set_last_name('Williams')
  employee.set_uid('111')
  
  # access setter methods
  print("New Values:")
  print('First name:', employee.get_first_name())
  print('Last name:', employee.get_last_name())
  print('Uid:', employee.get_uid())
  
# start out program
main()

## Passing arguments to constructor
class Employee:
  'Base class for Employees'
  
  def __init__(self, myFirstName, myLastName, myUID):
    self.first_name  = myFirstName 
    self.last_name = myLastName
    self.user_id = myUID
  
  def show_employee_details(self):
    print('First Name:', self.first_name)
    print('Last name:', self.last_name)
    print("user id:", self.user_id)
  
def main():
  # create new object
  employee = Employee('Nawraj', 'Lekhak','741')
  employee01 = Employee('Ram', 'Bhatt','101')
  
  # Display employee show_employee_details
  employee.show_employee_details()
  print()
  employee01.show_employee_details()
  
# start out program
main()  

## destructor. Also known as garbage collector.
class MyClass:
  
  def __init__(self):
    print("Create new object..")
    
  def __del__(self):
    print("Destroying used object..")
  
def main():
  # create new object
  object1 = MyClass()
  object2 = MyClass()
  object3 = MyClass()
  
  print("\n\n") ## create two new line 

  del(object1)
  del(object2)
  del(object3)
   
# start out program
main()  

## Public access modifier
class BankAccount:
  
  def __init__(self, my_initial_balance):
    self.balance = my_initial_balance
    
  def get_balance(self):
    return self.balance
    
def main():
  # Create new bank Account.
  account = BankAccount(500)
  
  #Display the current balance.
  print('Current balance is $', account.get_balance())
  
  ## Access object attribute value outside class
  account.balance += 800
  
  print('New balance is : $', account.get_balance())
  
# start out program
main()  
  

Encapsulation

-->
Using OOP in Python, we can restrict access to methods and variables. This prevent data from direct modification which is called encapsulation. In Python, we denote private attribute using underscore as prefix i.e single “ _ “ or double “ __“.

## Private access modifier
class BankAccount:
  
  def __init__(self, my_starting_balance):
    ## __ to make data attribute private to use within class
    self.__balance = my_starting_balance
    
  def get_balance(self):
    return self.__balance
    
  def deposit(self, amount_to_deposit):
    self.__balance += amount_to_deposit
    
def main():
  # Create new bank Account.
  account = BankAccount(500)
  
  account.deposit(400)
  
  #Display the starting balance.
  print('Starting balance is: $', account.get_balance())
  
  ## This will not be acessible
  print('Starting balance is: $', account.balance())
  
  account.balance += 4000
  print('Starting balance is: $', account.get_balance())
  
# start out program
main()  

# python child classes
class BankAccount:# Parent class
  def __init__(self, initial_balance):
    self.__balance = initial_balance
    
  def get_balance(self):
    return self.__balance
    
  def deposit(self,amount):
    self.__balance += amount
    
  def withdraw(self, amount):
    self.__balance -= amount
    
class SavingsAccount(BankAccount): # child class from BankAccount
  def __init__(self,initial_balance):
    BankAccount.__init__(self,initial_balance)
    
def main():
  # create savings account object
  savings_account = SavingsAccount(200)
  
  # Display current __balance
  print('SAving account balance $', savings_account.get_balance())
  savings_account.deposit(300)
  print('New account balance $', savings_account.get_balance())
  
# start program
main()

# python child classes
class BankAccount:# Parent class
  def __init__(self, initial_balance):
    self.__balance = initial_balance
    
  def get_balance(self):
    return self.__balance
    
  def deposit(self,amount):
    self.__balance += amount
    
  def withdraw(self, amount):
    self.__balance -= amount
    
class CheckingAccount(BankAccount): # child class
  
  def __init__(self,initial_balance):
    BankAccount.__init__(self,initial_balance)
    
def main():
  # create checking account object
  checking_account = CheckingAccount(300)
  
  # Display current __balance
  print('Initial account balance $', checking_account.get_balance())
  checking_account.deposit(300)
  print('New balance with deposit $', checking_account.get_balance())
  checking_account.withdraw(200)
  print('New balance after withdraw $', checking_account.get_balance())
  
# start program
main()

Inheritance

-->
Inheritance is a way of creating new class for using details of existing class without modifying it. The newly formed class is a derived class (or child class). Similarly, the existing class is a base class (or parent class).

# Inheritance. Class inherit from another class
class BankAccount:# Parent class
  def __init__(self, initial_balance):
    self.__balance = initial_balance
    
  def get_balance(self):
    return self.__balance
    
  def deposit(self,amount):
    self.__balance += amount
    
  def withdraw(self, amount):
    if amount > self.__balance:
      print("You do not have enough fund")
    else:
      self.__balance -= amount
  
def main():
  # create checking account object
  account = BankAccount(100)
  
  # Display current balance
  print('Initial account balance $', account.get_balance())
  account.deposit(300)
  print('New balance with deposit $', account.get_balance())
  account.withdraw(500)
  print('New balance after withdraw $', account.get_balance())
  
# start program
main()

# Import. 
import savings_account # name of file without extension
import checking_account 
  
def main():
  
  # create a saving and checking account
  
  mySavingsAccount = savings_account.SavingsAccount(100)
  myCheckingAccount = checking_account.CheckingAccount(500)
  
  # Display initial balance for both account
  print('SAving account balance $', mySavingsAccount.get_balance())
  print('Checking account balance $', myCheckingAccount.get_balance())
  
# start program
main()

--> Additionally, we use super() function before __init__() method. This is because we want to pull the content of __init__() method from the parent class into the child class

Polymorphism


-->
Polymorphism is an ability (in OOP) to use common interface for multiple form (data types).

-->
Suppose, we need to color a shape, there are multiple shape option (rectangle, square, circle). However we could use same method to color any shape. This concept is called Polymorphism.

# Overriding parent method within child class with same name. Also called polymorphism. 
class BankAccount:
  
  def __init__(self,myBalance):
    self.__balance = myBalance
    
  def get_balance(self):
    print('Calling the get_balance method from "Parent" class')
    
class CheckingAccount(BankAccount):
  def __init__(self, myBalance):
    BankAccount.__init__(self,myBalance)
    
  def get_balance(self):
    BankAccount.get_balance(self)## this will call parent class method. commenting this will call child class method
    print('Calling the get_balance method from "Child" class')
    
def main():
  account = CheckingAccount(50)
  account.get_balance()
  
main()

positional parameters and keyword parameters

if __name__ == ‘__main__’:
       main()

holds true if you are running script directly instead of import some module/file


python debugger :
python script.py -pdb
import pdb
pdb.set_trace()

Some useful ones to remember are:
  • b: set a breakpoint
  • c: continue debugging until you hit a breakpoint
  • s: step through the code
  • n: to go to next line of code
  • l: list source code for the current file (default: 11 lines including the line being executed)
  • u: navigate up a stack frame
  • d: navigate down a stack frame
  • p: to print the value of an expression in the current context

“To return both the key and value in a tuple, you can use the items() function:”

A decorator is a function that takes one function as input and returns another function

“Uses of _ and __ in Names
Names that begin and end with two underscores (__) are reserved for use within Python, so you should not use them with your own variables. This naming pattern was chosen because it seemed unlikely to be selected by application developers for their own variables.”
For instance, the name of a function is in the system variable function .__name__, and its documentation string is function .__doc__:”


Packages
We went from single lines of code, to multiline functions, to standalone programs, to multiple modules in the same directory. To allow Python applications to scale even more, you can organize modules into file hierarchies called packages.
Maybe we want different types of text forecasts: one for the next day and one for the next week. One way to structure this is to make a directory named sources, and create two modules within it: daily.py and weekly.py. Each has a function called forecast. The daily version returns a string, and the weekly version returns a list of seven strings.
Here’s the main program and the two modules. (The enumerate() function takes apart a list and feeds each item of the list to the for loop, adding a number to each item as a little bonus.)

from sources import daily, weekly”

“You’ll need one more thing in the sources directory: a file named __init__.py. This can be empty, but Python needs it to treat the directory containing it as a package”

“Let’s try again, this time including the special Python object initialization method __init__:
>>> class Person():
...     def __init__(self):
...         pass
This is what you’ll see in real Python class definitions. I admit that the __init__() and self look strange. __init__() is the special Python name for a method that initializes an individual object from its class definition. 1 The self argument specifies that it refers to the individual object itself.
When you define __init__() in a class definition, its first parameter should be self. Although self is not a reserved word in Python, it’s common usage. No one reading your code later (including you!) will need to guess what you meant if you use self.”

Inheritance:
Creating a new class from an existing class but with some additions or changes. It’s an excellent way to reuse code. When you use inheritance, the new class can automatically use all the code from the old class but without copying any of it.
You define only what you need to add or change in the new class, and this overrides the behaviour of the old class. The original class is called a parent, superclass, or base class; the ”“new class is called a child, subclass, or derived class. These terms are interchangeable in object-oriented programming.
So, let’s inherit something. We’ll define an empty class called Car. Next, define a subclass of Car called Yugo. You define a subclass by using the same class keyword but with the parent class name inside the parentheses (class Yugo(Car) below):
>>> class Car():
...     pass
...
>>> class Yugo(Car):
...     pass
...
Next, create an object from each class:
>>> give_me_a_car = Car()
>>> give_me_a_yugo = Yugo()”


“Without doing anything special, Yugo inherited the exclaim() method from Car. In fact, Yugo says that it is a Car, which might lead to an identity crisis. Let’s see what we can do about that.”

“Use super() when the child is doing something its own way but still needs something from the parent (as in real life).”

“The names of these methods begin and end with double underscores (__). You’ve already seen one: __init__ initializes a newly created object from its class definition and any arguments that were passed in.”

decorator:
a decorator is a function that takes another function as an argument , add some kind of functionality and return another function. all of this without altering the source code of original function that is passed.

@decorator_function
def display():
print(‘display function ran’)
is same as 
display = decorator_function(display)

__name__ : Every module in Python has a special attribute called __name__. It is a built-in variable that returns the name of the module.

__main__ : Like other programming languages, Python too has an execution entry point i.e. main. 'main' is the name of the scope in which top-level code executes. Basically you have two ways of using a Python module: Run it directly as a script, or import it. When a module is run as a script, its __name__ is set to __main__.
Thus,the value of __name__ attribute is set to __main__ when the module is run as main program. Otherwise the value of __name__ is set to contain the name of the module.
__file__ represents the file the code is executing from
os.path.dirname(__file__) gives you the directory the file is in
os.path.pardir stands for ".." which means one directory above the current one
os.path.join(os.path.dirname(__file__), os.path.pardir) joins the directory name and ".."
os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) resolves the above path and gives you an absolute path for the parent directory of the directory your file is in

With statement in Python
In Python you need to give access to a file by opening it. You can do it by using the open() function. Open returns a file object, which has methods and attributes for getting information about and manipulating the opened file. 
With statement
With the "With" statement, you get better syntax and exceptions handling. "The with statement simplifies exception handling by encapsulating common preparation and cleanup tasks."
In addition, it will automatically close the file. The with statement provides a way for ensuring that a clean-up is always used.
Without the with statement, we would write something like this:
file = open("welcome.txt")
data = file.read()
print data
file.close()  # It's important to close the file when you're done with it

With Statement Usage
Opening a file using with is as simple as: with open(filename) as file:
with open("welcome.txt") as file: # Use file to refer to the file object
   data = file.read()
   do something with data
Opens output.txt in write mode
with open('output.txt', 'w') as file:  # Use file to refer to the file object
    file.write('Hi there!')
Notice, that we didn't have to write "file.close()". That will automatically be called. 

#The @ symbol is used for class, function and method decorators.





-->

1 comment:

  1. Great Blog I Have Read Your Blog It Is Very Useful For Me Thank you for posting and sharing such great information.can you help me in finding out more detail on Fundamentals of Cryptography.

    ReplyDelete