#30 Python Programming – Modules, Package and Object-Oriented Programming

Python

Python

Python modules

  • Module is a python file which contains python data members i.e., statements and definitions.
  • A file containing Python code, for e.g.: fax.py, is called a module and its module name would be fax.
  • Like functions, modules help us to break down large python programs into small manageable and organized files. Furthermore, modules provide reusability of code.

How to create a module?

  • Module is a python script file, let us create one function definition below fax module
File : fax.py
def  calc(a1,a2):  # function calc will receive two arguments
          total=a1+a2
          print  “Sum of total value:”,total

Note : In the above code, there is no function call. It contains only definition.

How to import modules in Python?

 We can import the definitions present inside a module to another module or the interactive interpreter in Python.

  • We use the import keyword to do this. 
  • To import our previously defined module fax, we type the following in the Python prompt.

>>> import fax

· Using the module name we can access the function using dot (.) operation. for example:

>>> fax.calc(10,20)

30

>>>fax.calc(1.45,3.56)

5.01

· Within a module, the module’s name is available as the value of the global variable __name__. 

>>>fax.__name__

‘fax’

Advantage of modules

  • Code reusability
  • We may want to split single script (or) definition into multiple file for easier maintenance  
  • We can import a module on demand.
  • Each module has its own private symbol table, which is used as the global symbol table by all functions defined in the module.
  • Modules can import other modules
  • Open the below link will get list of python standard modules https://docs.python.org/3/py-modindex.html 
  • These files are in the Lib directory inside the location where you installed Python.
  • Standard modules can be imported the same way as we import our user-defined modules.
  •  There are various ways to import modules.

Python import statement

We can import a module using import statement and can access the definitions inside it using the dot operator as described above.

Here is an example.

# import statement example

# to import standard module math

import math

print(“The value of pi is”, math.pi)

When you run the program, the output will be:

The value of pi is 3.141592653589793

Import with renaming

  • We can import a module by renaming it as follows.
  • # import module by renaming it
  • import math as m     # alias (or) symbolic name
  • print(“The value of pi is”, m.pi)
  • We have renamed the math module as m
  • This can save us typing time in some cases.
  • Note that the name math is now not recognized in our scope.
  • Hence, math.pi is invalid, m.pi is the correct implementation.

Python from…import statement

  • We can import specific names from a module without importing the module as a whole. Here is an example.

# import only pi from math module

from math import pi

print(“The value of pi is”, pi)

  • We have imported only the attribute pi from the module.
  • In such case we don’t use the dot operator.
  • We can also import multiple attributes as follows.
>>> from math import pi, e
>>> pi
3.141592653589793
>>> e
2.718281828459045

Import all names

  •  We can import all names (definitions) from a module using the following construct.
  • # import all names from the standard module math
from math import *
print("The value of pi is", pi)
  • Now we have imported all the definitions from the math module.
  • This makes all names except those beginning with an underscore, visible in our scope.
  • However, Importing everything with the asterisk (*) symbol is not a good programming practice.
  • This can lead to duplicate definitions for an identifier. It also hampers the readability of our code.

Python Module Search Path

  • While importing a module, Python looks at several places.
  • Interpreter first looks for a built-in module then (if not found) into a list of directories defined in sys.path.
  • The search is performed in this order.
    •  The current directory.
    • PYTHONPATH (an environment variable with a list of directory).
    • The installation-dependent default directory.

What is sys.path ?

C:\Users\python>python
Python 2.7.13 (v2.7.13:a06454b1afa1, Dec 17 2016, 20:42:59) [MSC v.1500 32 bit (
Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', 'C:\\Python27\\Lib', 'C:\\Python27\\DLLs', 'C:\\Python27\\Lib\\lib-tk',
'C:\\Users\\python', 'C:\\Windows\\system32\\python27.zip',
 'C:\\Python27\\lib\\plat-win', 'C:\\Python27', 'C:\\Python27\\lib\\site-packages']
>>>
>>>

Module path list in linux os

Example :

root@awadheshpdwivedi]#  pwd

/root/python/modules/example

Creating one module file name called report.py

File : report.py

import os
def  display() :
         print “Current vmreport status :-“
        os.system(“vmstat”)
        print “Current CPU Load balance:-“
        os.system(“uptime”)

Importing report.py file into an external file

File : p1.py

import  report

report.display()

root@awadheshpdwivedi]#  ls

p1.py  report.py

root@awadheshpdwivedi]# python p1.py

#  display vmstat and uptime command result to the screen

  •  After completing the execution, again type ls command from linux command line (or) dir command in windows operating system.

root@awadheshpdwivedi]#  ls

p1.py  report.py report.pyc

· The report.pyc file contain a ready-“byte-compiled” version of the module report.

· Test the file type in linux command line

root@awadheshpdwivedi]#  file  report.pyc   

# file is a linux command – it’s determine file type

report.pyc :python 2.7 byte-compiled

· Normally, you don’t need to do anything to create the report.pyc file.

· Whenever report.py is successfully compiled, an attempt is made to write the compiled version to report.pyc.

· Now let us create one sub directory under your current working directory.

root@awadheshpdwivedi]#  mkdir L1

root@awadheshpdwivedi]# ls

L1  p1.py report.py report.pyc

· Now let us move the report.py file into L1 sub directory

root@awadheshpdwivedi]# mv report.py L1

root@awadheshpdwivedi]# ls

L1 p1.py  report.pyc

· Now there is no report.py file , but report.pyc file is available in current working directory. So script (p1.py ) will execute as follows.

root@awadheshpdwivedi]# python p1.py

# Display system commands

# Execution done successfully

 #Deleting report.pyc file from current working directory

root@awadheshpdwivedi]# rm report.pyc

L1 p1.py

· Now there is no report.py and report.pyc files in my current working directory.

· If you run the p1.py file now, we will get an error message.

· We can’t always keep module file and script files in same directory.

· But sometimes we load external modules or in built module such as os or sys.The os.py or sys.py  files are not in our current working directory.

. If we import os module in to script , the file loads successfully. Let’s see them in detail.

Example :

root@awadheshpdwivedi]# ls

L1 p1.py  

· Now let us create one more file name called p2.py

File :p2.py

import os

os.system(“uptime”)

· Before executing this file (p2.py) ,list out all the available files.

root@awadheshpdwivedi]# ls

L1 p1.py  p2.py

· Note down there is no os.py or os.pyc file in our current working directory. But if we run the program p2.py  it will execute successfully, there is no import or attribute errors

root@awadheshpdwivedi]#  python p2.py

# Execute successfully

# display uptime command result to #screen.

  • The reason behind is that, whenever we are interpreting python file, interpreter searches the module file from sys.path variable .
  • The sys.path is list type of variable.
  • This is equivalent to $PATH in shell command line ,@INC in perl script , $LOAD_PATH in ruby.

root@awadheshpdwivedi]# python

import sys

sys.path

  • Look at the result which is list type of data structure. So we can use all list functions and list manipulation.
  • The sys.path variable contains the current directory (. ) , installed python path.
  • We can use append or insert functions to insert our external module file into sys.path.

Example

File : p3.py

import  sys

sys.path.append(“/root/python/modules/L1”)

import report

report.display()

root@krosumlabs day1]#  ls

L1  p1.py p2.py p3.py

# note there is no report.py file

  • We added (appended) L1 directory ( which contains module files) into sys.path variable ,so now python interpreter look at (“/root/python/modules/day1/L1) where report.py file is present. So it will load successfully.
  • If you want to make permanent update use PYTHONPATH variable.

What is PYTHONPATH ?

  • The PYTHONPATH is an environment variable, consisting of a list of directories.
  • The syntax of PYTHONPATH is the same as that of the shell variable PATH.
  • Here is a typical PYTHONPATH from a Windows system ?

set PYTHONPATH = c:\python20\lib;

· In linux operating system

open a  login profile file (.profile or .bash_rc )

export PYTHONPATH=”/root/python/modules/day1/L1”

Now open new shell , test the belowscript

File :p4.py

import report

report.display()

  • Run the p4.py file , the script will execute successfully.
  • Compare p3.py and p4.py file, understand the sys.path and PYTHONPATH variable usages.

Reloading a module

  • The Python interpreter imports a module only once during a session.
  • This makes things more efficient.
  • Suppose we have the following code in a module named my_module.
# This module shows the effect of
#  multiple imports and reload
  print("This code got executed")

Now we see the effect of multiple imports.

>>> import my_module
This code got executed
>>> import my_module
>>> import my_module
  • We can see that our code got executed only once. This goes to say that our module was imported only once.
  • Now if our module changed during the course of the program, we would have to reload it.
  • One way to do this is to restart the interpreter. But this does not help much.
  • Python provides a neat way of doing this. We can use the reload() function inside the imp module to reload a module. This is how its done.
>>> import imp
>>> import my_module
This code got executed
>>> import my_module
>>> imp.reload(my_module)
This code got executed

dir() built-in function

  • We can use the dir() function to find out names that are defined inside a module.
  • For example, we have defined a function add() in the module example that we had in the beginning.
>>> dir(example)
['__builtins__',
'__cached__',
'__doc__',
'__file__',
'__initializing__',
'__loader__',
'__name__',
'__package__',
'add']
  • Here, we can see a sorted list of names (along with add).
  • All other names that begin with an underscore are default Python attributes associated with the module (we did not define them our self).
  • For example, the_name_attribute contains the name of the module.
>>> import example
>>> example.__name__
'example'
  • All the names defined in our current namespace can be found out using the dir() function without any arguments.
>>> a = 1
>>> b = "hello"
>>> import math
>>> dir()
['__builtins__', '__doc__', '__name__', 'a', 'b', 'math', 'pyscripter']

Python Package

  • Python package is a hierarchical file directory structure.
  • We don’t usually store all of our files in our computer in the same location.
  • We use a well-organized hierarchy of directories for easier access.
  • Similar files are kept in the same directory, for example, we may keep all the songs in the “music” directory.
  • Analogous to this, Python has packages for directories and modules for files.
  • As our application program grows larger in size with a lot of modules, we place similar modules in one package and different modules in different packages.
  • This makes a project (program) easy to manage and conceptually clear.
  • Similar, as a directory can contain sub-directories and files, a Python package can have sub-packages and modules.
  • A directory must contain a file named init.py in order for Python to consider it as a package.
  • This file can be left empty but we generally place the initialization code for that package in this file.

Package structure

  • Create a directory Demo and under the Demo directory let us create a package structure that holds the below format.
root@awadhesh ~]#mkdir  Demo
root@awadhesh  ~] # cd Demo
root@awadhesh  Demo]#mkdir   Fax
  • Under Fax directory there are three python scripts such as p1.py ,p2.py and p3.py.
  • Each file contains several functions. Instead of loading these individual files and functions, we are creating Fax as a package which will initialize all the functions under init.py file.
  • We can import modules from a package using the dot (.) operator. To import particular names from a module directory , use following syntax :-
from module import functions

from module import functions

Go to Fax directory

root@awadhesh Demo]#   cd   Fax
 
· Create an initialization file __init__.py.
root@awadhesh  Fax]#  vi __init__.py
from p1.py import f1,f2,f3
from p2.py import calc,bc
from p3.py import view,report,result
# save and exit
  • Now move to the parent directory Demo and create one new python script file,say demo_package.py and import the package Fax inside it.
root@awadhesh Demo]#   vi  demo_package.py
import  Fax  #  Now Fax is a package which encapsulates a collection of modules and methods
Fax.f1()  # to invoke f1() method
Fax.calc()   # to invoke calc() method
  • While importing packages, Python looks in to the list of directories defined in sys.path, similar to module search path.

Object Oriented Programming in Python

  • Python is an object oriented programming language.
  • It is a way of organizing the program, which is to combine the data and functionality and wrap them inside something called an object.
  • Classes and objects are the two main aspects of object oriented programming.
  • The class becomes a new type where objects are their instances.
  • The class defines a data type, which contains variables, properties and methods.
  • A class describes the abstract characteristics of a real-life thing.
  • Object is simply a collection of data (variables) and methods (functions) that act on those data whereas class is a blueprint for the object.

Defining a Class in Python

  • In Python, we define a class using the keyword class.
  • Class which contains collection of variables and methods preferably called as data members.
  • A class creates a new local namespace where all its data members are defined.
>>> class Box:
...            pass
>>> Box
>>> type(Box)
<__main__.Box instance at 0x0247D440>

Creating an Object in Python

  • A class is used to create new object instances (instantiation).
  • These class objects allow us to access the different attributes of the class.
  • The procedure to create an object looks very similar to a function call.

Syntax:-

Example :-

>>> class Box:
...        name="Krosum"
>>> obj=Box()
>>> type(obj)
>>> obj.name
'Krosum'
obj.name is interpreted as Box.name
 >>> class Box:
...         name="Krosum"
...         code=34
...         ips=['10.20.30.40','10.20.30.50']
...         books={'subject1':'programming python','subject2':'Python Essentials'}
 

>>> obj=Box()
>>> type(obj.name)
>>> type(obj.code)
>>> type(obj.ips)
>>> type(obj.books)
>>> print "Hello",obj.name
Hello Krosum
>>> print "Total no.of IPs",len(obj.ips)
Total no.of IPs 2
>>> # adding new items to the existing list
>>> obj.ips.append("127.0.0.1")
>>> # List of updated IPs
>>> print obj.ips
['10.20.30.40', '10.20.30.50', '127.0.0.1']
>>> #accessing dictionary values
>>> print obj.books['subject1']
Programming python
>>> # modifying existing dictionary value
>>> obj.books['subject1']="PYTHON PROGRAMMING GUIDE"
>>> # adding new subject to the existing dictionary
>>> obj.books['subject3']="Network Python Program"
>>> # List of all the keys(subjects) from dictionary (books)
>>> obj.books.keys()
['subject1', 'subject2', 'subject3']
# List of all the values (subjects) from dictionary (books)
>>> obj.books.values()
['PYTHON PROGRAMMING GUIDE', 'Python Essentials', 'Network Python Program']

From the above examples, we have accessed the class data members through the class instance (object).

We can create more than one object from a single class.

Each such object contains an individual reference (memory).

>>> class Box:
...        name=""
...        fname=""
 
>>> obj1=Box()    # object1 is created
>>> obj2=Box()    # object2 is created
>>> obj3=Box()    # object3 is created
>>> obj1.name="User A"
>>> obj1.fname="/etc/passwd"
>>> obj2.name="User B"
>>> obj2.fname="/var/log/auth.log"
>>> obj3.name="User C"
>>> obj3.fname="/home/userC/temp.txt"
>>> print "User Name:",obj1.name,"\tFile name:",obj1.fname
User Name: User A       File name: /etc/passwd
>>> print "User Name:",obj2.name,"\tFile name:",obj2.fname
User Name: User B       File name: /var/log/auth.log
>>> print "User Name:",obj3.name,"\tFile name:",obj3.fname
User Name: User C       File name: /home/userC/temp.txt
Now let us see how to access class function (methods) using class instance.
>>> class Box:
...        name="Krosum"
...        DBs=['oracle','sql','mysql']
 

...     def    hello():
...             print "Im hello function from Box class"
>>>
>>> obj=Box()
>>> obj.name
'Krosum'
>>> obj.DBs
['oracle', 'sql', 'mysql']
>>> obj.DBs[-1]
'mysql'
>>> obj.hello()
Traceback (most recent call last):
  File "", line 1, in
TypeError: hello() takes no arguments (1 given)
>>>
Here, we didn’t pass any argument but it displays ‘ TypeError: hello () takes no arguments (1 given)’
Whenever an object calls its method, the object itself is passed as the first argument, i.e) the form object.method() is internally translated into ClassName.method(object).
>>> obj.hello()    #  Box.hello(obj)   # like function call with single argument
In general, calling a method with a list of n arguments is equivalent to calling the corresponding function with an argument list with method's object as its first argument.
For these reasons, the first argument of the function inside class (method definition) must be the object itself.
This is conventionally called self.
 
>>> class Box:
...     def hello(self):
...             print "Im hello method from Box class"
>>> obj=Box()
>>> obj.hello()
Im hello function from Box class
Note that an the argument (self) is passed in the method definition inside the class but still, we called the method simply as object.hello() without any arguments.
Here, obj.hello()  is interpreted as  hello(obj)
>>> class Box:
...            def hello(self):
...                   print "Im hello function",self
>>>
>>> obj=Box()
>>> obj.hello()
Im hello function <__main__.Box instance at 0x023DD620>
>>> obj
<__main__.Box instance at 0x023DD620>
Note the above result. Both self and obj refers to same location.
So self is just an object placeholder.
obj.name is same as  self.name
>>> class Box:
...           name="Vikas"
...           def hello(self):
...                     print "From hello function",self.name
>>>
>>> obj=Box()     # creating object
>>> obj.hello()  # object.method(  )
From hello function Vikas
We have already discussed that the class variables can be accessed only through class name or class instance.
>>> class Box:
...             name="Krosum"
...             def hello(self):
...                       print "Hello function",name  # Display Name Error
>>> obj=Box()
>>> obj.hello()
Hello function
Traceback (most recent call last):
  File "", line 1, in
  File "", line 4, in hello
NameError: global name 'name' is not defined
 
Here the class variable name has to be accessed as self.name
Variables in python are generally classified as Class variables, Instance variables and Private variables.
 
Class variables 
Class variables are defined only within a class definition.
We can access and use a class variable directly inside the class.
Example:
>>> class Box:
...        username="Krosum"    
...        Filename="/etc/passwd"
...     print "From Box Class username:",username
...     print "From Box Class Filename:",Filename
From Box Class username: Krosum
From Box Class Filename: /etc/passwd
From the above example username and Filename are class variables which are placed inside a class directly.
We can access class variables from outside the class definition, using class name or class instance (object)
 
Example :
>>> class Box:
...        username="Krosum"    
...        Filename="/etc/passwd"
>>> obj=Box()
>>> print “username:”,obj.username
Krosum
>>> print “Filename:”,obj.Filename
/etc/passwd
>>> print “username:”,Box.username
Krosum
>>>print “Filename:”,Box.Filename
/etc/passwd

Instance variable

  • Instance variables are the variables placed inside the class member function.
  • We can use instance variable only within the class member function.
  • We can’t use instance variable either out of the class or out of the class member functions.

Examples

class Box:
     book="Core Python Programming" # class variable
     def getdata(self,author,price,vol):   
             print "Book name:",self.book
             print "Author name:",author
             print "Price:",price
             print "Volume:",vol
#  author , price, vol are instance variables
# we can’t use instance variables outside the class function
 
 obj=Box()
 obj.getdata ("Mr.Guido van rossum",467.67,1.0)
 
Book name: Core Python Programming
Author name: Mr.Guido van rossum
Price: 467.67
Volume: 1.0
 
=> We can initialize instance variable to class variable
class Box:
        name="Mr.Krosum" # class varaible
        price=""     # class variable
        volume=""   # class variable
        def getdata(self,p,v):
                self.price=p    # instance --> class variable
                self.volume=v  # instance --> class variable
       def display(self):
             print "User name:",self.name # class variable
             print "Price name:",self.price # class variable
             print "Voulme:",self.volume # class variable
 

obj=Box()
obj.getdata(1000.34,1.0)
obj.display()
# Run the above script in command line
C:\Users\KrosumLabs>python p1.py
User name: Mr.Krosum
Price name: 1000.34
Volume: 1.0

Private variable

Class variable that begins with double underscore (__) are called special variable as they have special meaning

>>> class Emp:
...     name="Mr.Xerox"
...     __password="abc"       #  private variable  can’t be accessed from    outside the class
...     def display(self):
...             print self.name
...             print self.__password
 
>>> obj=Emp()
>>> obj.display()
Mr.Xerox
abc
>>> obj.name
'Mr.Xerox'
>>> obj.__password
Traceback (most recent call last):
  File "", line 1, in
AttributeError: Emp instance has no attribute '__password'

Constructors in Python

  • A class functions that begins with double underscore (__) are called special functions as they have special meaning.
  • Of one particular interest is the __init__()function. This special function gets called whenever a new object of that class is instantiated.
  • This type of function is also called constructors in Object Oriented Programming (OOP). We normally use it to initialize all the variables.
Example  1:-
>>> class Box:
                    def display(self):
                    print "Im display function"
 
>>> obj=Box()
>>> obj.display() # object.method() call
Im display function
 
Example 2:-
>>> class Box:
                  def __init__(self):
                          print   "Im Initialization function block"
>>> obj=Box()
Im Initialization function block  ## didn't used object.method(), __init__() automatically initialized
 

Example 3:-
>>> class Emp:
                name=" "
                dept=" "
                def   __init__(self):   # automatically initialized constructor block
                          self.name="Mr.John Paul"
                         self.dept="Sales"
                def   display(self):    # non  constructor block
                           print  "Emp Details:"
                          print "Name:",self.name
                          print "Dept:",self.dept
 
>>> obj=Emp()   # object  is initialized
>>> obj.display()   # object.method() call
Emp Details:
Name: Mr.John Paul
Dept: Sales

Destructors in Python

  • Destructors are called when an object gets destroyed.
  • The destructor is defined using __del__(self).
  • __del__(self)  method is only called on destruction of the object.
>>> class Box:
                 def __init__(self):
                        print "Im Constructor" 
                def __del__(self):
                        print   "Im Destructor"
>>> obj=Box()
Im Constructor 
>>> del(obj)
  Im Destructor
  • In the example, the obj is created and manually deleted; therefore, both messages will be displayed.
  • However, if you comment out the last line  del (obj), the destructor will not be called immediately.
  • Instead, the Garbage Collector (GC) will take care of the deletion automatically.

Python Inheritance

What is inheritance?

  • Inheritance is one of the OOPs feature.
  • The Reusability of existing class means that it refers to the concept of defining a new class with little or no modification to an existing class.
  • The new class is called child class or derived class.
  • Existing class is called parent class or base class.

Python Inheritance Syntax

class BaseClass :

           Body of base class
class DerivedClass ( BaseClass ):

          Body of derived class
  • Derived class inherits features from the base class, adding new features to it.
  • This results into re-usability of code.

Example :

import  os
class  Parent:
          def   display(self):
                   print    “Display mounted file system details :”
                   os.system(“df –Th”)
class Child(Parent) :   # Inheritance – Extending Parent class data members
          def  view(self):
                   print  “Current process details :-“
                   os.system(“ps –f”)
obj=Child()
obj.view()
obj.display()  # using child class object, calling parent data member
 
Example :
class  Parent :
def   display(self):
                    print “This is display block from Parent class”
 
class Child(Parent):
          def   display(self):
                   print “This is display block from Child class”
 
obj=Child()
obj.display()  #  This is display block from Child class

Method Overriding in Python

  • In the above example, notice that display( )method was defined in both classes, Parent as well Child.
  • When this happens, the method in the derived class overrides that in the base class.
  • Generally when overriding a base method, we tend to extend the definition.
  • The same is being done by calling the method in base class from the one in derived class.
class Parent():
          def display(self):
                      print “This is display block from Parent class”
class Child(P):
          def display(self):
                     print “This is display block from Child class”
                   Parent.display (self)   # calling Parent class display method
obj=Child()
obj.display() 

Python Multiple Inheritance

  • A class can be derived from more than one base classes, this is called multiple inheritance
  • In multiple inheritances, the features of all the base classes are inherited into the derived class.

Syntax

class Parent1:
           pass
class Parent2:
          pass
class Child(Parent1, Parent2):
           pass
  • The class Child inherits from both Parent1 and Parent2.

Multilevel Inheritance in Python

  • We can also inherit the features from a derived class. This is called multilevel inheritance. It can be of any depth in Python.
  • In multilevel inheritance, features of the base class and the derived class are inherited into the new derived class.

Syntax

class Parent1:
          pass
class Parent2(Parent1):
          pass
class Child(Parent2):
          pass

Here, Parent2 is derived from Parent1, and Child is derived from Parent2.

Using super() method  

class GrandParent(object):                                                      
         def act(self):                                                              
              print 'grandpa act'                                                     
class Parent(GrandParent):                                                      
         def act(self):                                                              
              print 'parent act'                                                      
class Child(Parent):                                                            
        def act(self):                                                              
              super (Child.__bases__[0], self) . act()                                   
              print 'child act'                                                       
 
instance = Child()                                                              
instance.act()

Built-In Class Attributes

  • Every Python class keeps the following built-in attributes and  they can be accessed using dot operator like any other attribute.
  •  __dict__         Dictionary containing the class’s namespace.
  •  __doc__         Class documentation string or none, if undefined.
  • __name__      Class name.
  • __module__   Module name in which the class is defined. ( This attribute is “__main__” in interactive mode.)
  • __bases__     A possibly empty tuple containing the base classes,in the order of their occurrence in the base class list.

Example 1 :

>>> class Box:
...     ' sample class document, this is Box class'
>>> Box.__doc__
' sample class document, this is Box class'
 
>>> Box.__dict__
{'__module__': '__main__', '__doc__': ' sample class document, this is Box class'}

Example 2 :

>>> class Box:
...     ' sample document message from Box class'
...     count=10
...     LIST=['data1','data2']
...
>>> Box.__doc__
' sample document message from Box class'
>>>
>>> Box.__dict__
{'count': 10, '__module__': '__main__', 'LIST': ['data1', 'data2'], '__doc__': '
 sample document message from Box class'}
>>> Box.__name__
'Box'
>>>
>>> Box.__module__
'__main__'
>>> Box.__bases__
()
>>>

 

Example 3:

>>> class Parent:
... pass
>>> class Child1(Parent):
... pass
>>> class Child2(Child1):
... pass
>>> obj=Child2()
>>>
>>> Child2.__bases__
>>> Child1.__bases__
>>> Parent.__bases__
()

Example 4:

File : Fax.py

class C1:
          def f1(self):
                   print "f1 func"
Import Fax.py file into external python file ( p2.py )
File: p2.py
import Fax
obj=Fax.C1()
obj.f1()
print Fax.C1.__name__  
print Fax.C1.__module__
Run the above (p2.py) file in command line.
C:\Users\awadheshpdwivedi>python   p2.py
f1 func
C1
Fax

Example 5:

  • __name__ is a variable automatically set in an executing python program.
>>> print __name__
__main__
>>>
  • If you import your module from another program, __name__ will be set to the name of the module.
  • If you run your program directly, __name__ will be set to __main__.
if __name__ == "__main__name___" :
  # will run only if module directly runs
             print  "I am being run directly"
else:
  # will run only if module imported
             print  "I am being imported" 

Python Iterators

  • Iterator in Python is simply an object that can be iterated upon.
  • An object which will return data, one element at a time.
  • Python iterator object must implement two special methods,

      __iter__()and __next__(), collectively called the iterator protocol.

  • An object is called iterable if we can get an iterator from it.

Most of built-in containers in Python like: list, tuple, string etc. are iterables.

  • The iter() function (which in turn calls the __iter__() method) returns an iterator from them.
>>> dbs=['oracle','sql','db2','server2000']
>>> obj=iter(dbs)
>>> obj    
>>> for v in obj:
               v
'oracle'
'sql'
'db2'
'server2000'
=>   We use the next() function to manually iterate through all the items of an iterator.
=>   When we reach the end and there is no more data to be returned,
       it will raise StopIteration
 
>>> dbs=['oracle','sql','db2','server2000']
>>> obj=iter(dbs)
>>> next(obj)
'oracle'
>>> next(obj)
'sql'
>>> next(obj)
'db2'
>>> next(obj)
'server2000'
>>> next(obj)
Traceback (most recent call last):
  File "", line 1, in
StopIteration 

Python Generator

  • A generator is a function that returns an object (iterator) which we can iterate over (one value at a time).

How to create a generator in Python?

  • It is as easy as defining a normal function with yield statement instead of a return statement.
  • If a function contains at least one yield statement (it may contain other yield or returnstatements),it becomes a generator function.
  • Both yield and return will return some value from a function.
  • The difference is that, while a return statement terminates a function entirely, yield statement pauses the function saving all its states and later continues from there on successive calls.

Differences between Generator function and a Normal function

  • Here is how a generator function differs from a normal function.
  • Generator function contains one or more yield statement.
  • When called, it returns an object (iterator) but does not start execution immediately.
  • Methods like __iter__() and __next__() are implemented automatically.

So we can iterate through the items using next().

  • Once the function yields, the function is paused and the control is transferred to the caller.
  • Local variables and their states are remembered between successive calls.
  • Finally, when the function terminates, StopIteration is raised automatically on further calls.

Example

>>> def   f1():
...          n=1
...          yield n
...          n+=1
...          print "2nd n"
...          yield n
>>>      f1()
 
>>> r1=f1()
>>> r1
>>>
>>> next(r1)
1
>>> next(r1)
2nd n
2
>>> next(r1)

Traceback (most recent call last):
  File "", line 1, in
StopIteration
  • Unlike normal functions, the local variables are not destroyed when the function yields.
  • Furthermore, the generator object can be iterated only once.

Python Generators with a Loop

  • Normally, generator functions are implemented with a loop having a suitable terminating condition.
  • Let’s take an example of a generator that reverses a string.
def    rev_str(my_str):
           length = len(my_str)
           for i in range(length - 1,-1,-1):
                      yield  my_str[i]
 
         for char in rev_str("hello"):
                         print(char)

# For loop to reverse the string

# Output:

# o

# l

# l

# e

# h

Why generators in Python?

There are several reasons which make generators an attractive implementation to go for.

1.  Easy to Implement

  •  Generators can be implemented in a clear and concise way as compared to their iterator class counterpart.
  • Following is an example to implement a sequence of power of 2’s using iterator class.
class   PowTwo:
    def    __init__ self, max = 0 :
             self.max = max
 
    def     __iter__(self):
             self.n = 0
            return self
 
    def   __next__(self):
          if   self.n > self.max:
                 raise StopIteration
 
        result = 2 ** self.n
        self.n += 1
        return result
  • This was lengthy. Now let’s do the same using a generator function.
def    PowTwoGen(max = 0):
         n = 0
        while n < max>
               yield 2 ** n
               n += 1
  • Since, generators keep track of details automatically; it was concise and much cleaner in implementation.

2.  Memory Efficient

  • A normal function to return a sequence will create the entire sequence in memory before returning the result. This is overkill if the number of items in the sequence is very large.
  • Generator implementation of such sequence is memory friendly and is preferred since it only produces one item at a time.

3. Represent Infinite Stream

  • Generators are excellent medium to represent an infinite stream of data.
  • Infinite streams cannot be stored in memory and since generators produce only one item at a time, it can represent infinite stream of data.
  • The following example can generate all the even numbers (at least in theory).
def    all_even():
           n = 0
           while True:
                 yield n
                 n += 2

4.  Pipelining Generators

  • Generators can be used to pipeline a series of operations. This is best illustrated using an example.
  • Suppose we have a log file from a famous fast food chain. The log file has a column (4th column) that keeps track of the number of pizza being sold every hour and we want to sum it to find the total pizzas sold in 5 years.
  • Assume everything is in string and numbers that are not available are

marked as ‘N/A’.

A generator implementation of this could be as follows.

with  open('sells.log') as file:
         pizza_col  =  (line[3] for line in file)
         per_hour  =  (int(x) for x in pizza_col if x != 'N/A')
         print("Total pizzas sold  =  ",sum(per_hour))

Python Generator Expression

  • Simple generators can be easily created on the fly using generator expressions. It makes building generators easy.
  • Same as lambda function creates an anonymous function, generator expression creates an anonymous generator function.
  • The syntax for generator expression is similar to that of a list comprehension in Python. But the square brackets are replaced with round parentheses.
  • The major difference between a list comprehension and a generator expression is that while list comprehension        produces the entire list, generator expression produces one item at a time.
# Initialize the list
my_list = [1, 3, 6, 10]

# square each term using list comprehension

[x**2 for x in my_list]

# Output: [1, 9, 36, 100]
# same thing can be done using generator expression
(x**2 for x in my_list)

# Output:

generator object genexpr at 0x0000000002EBDAF8

Reference

https://www.python.org/

https://www.anaconda.com/

Continue exploring at Teknonauts.com

Leave a Reply

Your email address will not be published. Required fields are marked *