Thursday, 27 April 2017

Closures in python

what is the Closure function:

defining the normal function  by satisfying following rules is known as a closure function.

  • must be define a nested function(function inside function).
  • nested function must refer to a value defined in the Enclosing function.
  • the enclosing function must return the nested function.

def print_message(msg): ---> enclosing function

    def printer( ):  ----> nested function

        print(msg)

    return printer

message=print_message("hello siva")

message( )

hello siva.

After removing the enclosing function  namespace/object still we can call nested function.

def print_message(msg):

    def printer( ):

        print(msg)

    return printer

message=print_message("hello siva")

message( )

del print_message

message( )

hello siva

hello siva

Example:

def make_multiplier_of(n):

    def multiplier(x):

        return x*n

    return multiplier

times3=make_multiplier_of(3)

times5=make_multiplier_of(5)

print(times3(9))

print(times5(3))

print(times5(times3(2)))


27

15

30

Example:

x=10
def f1( ):
    y=20
    print(x)
    print(y)
def f2( ):
    z=30
    print(x)
    print(z)
f1( )
f2( )

  • Whenever f1( ) is executed over, y value is erased/removed from the memory location, here y is local variable, this is the one problem in functions.
  • to solve the above problem, we are going to use the concept called  global variables, global variable value is modified some other time, this is the problem.
  • to overcome above problem,we are going to use the concept called closure function.
Closures can be used in the following  situations:
  • closures can avoid the use of global variable values and provides some from data hiding.
  • instead of defining the class with one method define the closure function.

Wednesday, 26 April 2017

Generators in python


  • Implementing the iterator, it is a toughest mechanism.
  • Implement the iterator mechanism in easy way or simplified manner we are going to use the concept called Generator.

Generators:

  • Generator is a function, which contains one or more yield statements.
  • yield statement is also like as a return statement in normal function,that yield statement return's value.
  • when ever return statement of a function is executed ,function terminates.
  • when ever yield statement of a function is executed,function execution will be paused later we can continue the executes the remaining part of the function.
  • when ever we call the generator function it internally returns the generator object.
  • generator object is also same like as a iterator object.
  • when ever we call the Generator function internally                  _ _iter_ _( ) method and _ _next_ _( ) method are implemented  automatically.
  • when ever we call the next( ) function on the generator object ,generator function logic will be executed until yield statements.
  • if no more elements in generator object,it raises StopIterationException when ever we call next( ).



def my_gen( ):
    n=1
    print("this is printed first")
    yield n

    n += 1
    print("this is printed second")
    yield n

    n += 1
    print("this is printed last")
    yield n
a=my_gen( )
print(a)
print(next(a))
print(next(a))
print(next(a))
print(next(a))

<generator object my_gen at 0x000000A7EBD0C7D8>
this is printed first
1
this is printed second
2
this is printed last
3
Traceback (most recent call last):
  File "E:/python_practice/examples/generator.py", line 18, in <module>
    print(next(a))
StopIteration

Note:
  • Handling the StopIterationException by using For loop .

def my_gen( ):
    n=1
    print("this is printd first")
    yield n

    n += 1
    print("this is printed second")
    yield n

    n += 1
    print("this is printed last")
    yield n
a=my_gen( )
for p in a:
    print(p)


this is printd first
1
this is printed second
2
this is printed last
3

Example: reversing the string

def rev_str(my_str):

    length=len(my_str)

    for i in range(length-1,-1,-1):

        yield my_str[i]

x=rev_str("siva")

for p in x:

    print(p)


a
v
i
s


Tuesday, 25 April 2017

Iterators in python

Python Iterators:


  • Iterator in python is simply an object that can be iterated upon,on top of the iterator object we can apply the iterations.
  • in order to get the iterator object on any class object that class should contain _ _iter_ _( ) method and _ _next_ _( ) method collectively called the iterator protocol.
  • we can get the iterator object on any class object by calling iter( ) function.
  • we can get the one by one element from the iterator object by calling next( ) function.
  • no elements are there in the iterator object  next ( ) function returns stopIterationException .
  • builtin containers in python like: list,tuple,string, ... are iterables.

Iterating Through an Iterator in Python:



my_list = [4, 7, 0, 3]

# get an iterator using iter()

my_iter = iter(my_list)

# iterate through it using next() 

print(next(my_iter))

print(next(my_iter))

print(next(my_iter))

print(next(my_iter))

print(next(my_iter))




4

7

0

3

Traceback (most recent call last):

  File "E:/python_practice/examples/iter.py", line 9, in 

<module>  print(next(my_iter))

StopIteration


By using For loop:

my_list=[4,7,0,3]

for item in my_list:

    print(item)


4

7

0

3


How for loop actually works?



for element in iterable:

    iter_obj = iter(iterable)

    while True:

        try:

            element = next(iter_obj)

        except StopIteration:

                break

Building your own iterator in python:


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:

            result= 2**self.n

            self.n +=1

            return result

        else:

            raise StopIteration

a=powtwo(4)

i=iter(a)

print(next(i))

print(next(i))

print(next(i))

print(next(i))

print(next(i))

print(next(i))

1
2
4
8
16
Traceback (most recent call last):
  File "E:\python_practice\examples\own iterator.py", line 21, in <module>
    print(next(i))
  File "E:\python_practice\examples\own iterator.py", line 13, in __next__
    raise StopIteration

StopIteration



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:

            result= 2**self.n

            self.n +=1

            return result

        else:

            raise StopIteration

for i in powtwo(4):

    print(i)



1
2
4
8
16

Advantages:

cleaner code.

iterators can work with infinite sequences.

iterators saver resources like memory management,power management,process management,....

for large data sets,iterators saves both time and space

Monday, 24 April 2017

Multi Threading

Multi Threading:

Processor:
  • processor is a electronic circuit, it understands only power signals,they are:
ON  ---> 1
OFF ---> 0
  • electronic circuit never executes multiple statements/process/applications at a time,it executes one after another.
Thread:
  • Thread is a functionality/logic which executes simultaneously along with the other part of the program. 
                                               (or)
  • Thread is a light-weight process.
  • Executing the statements one with another(not completely parallel) some part of the program execution.
Process:
  • Any program which is under execution is known as process.
Note:
  • upto python2.4, high level threading concepts are not implemented.
  • after python2.4, high level threading concepts are implemented similar to java and .net programming threading concepts.
Defining the functionality as a Thread:-

  • We can define the functionality as a Thread by overriding    run( ) method of Thread class.
  • Thread is a pre-defined class, which is defining Threading module.

import threading

class X(threading.Thread):

    def run(self):

        for p in range(10):
            print(p)
x1=X( )
x1.start( )
for z in range(10,20):
    print(z)

010

111

212

313

414

515

616

717

818

919

  • If we call the run( ) method directly it will be executed as a normal method.
  • In-order to execute the run( ) method logic as a Thread we have to call the run( ) method through the start( ) method of Thread class.
  • By default python interpreter creates one Thread that is main( ) Thread.

The threading module exposes all the methods of the thread module and provides some additional methods:
  • threading.activeCount( ): Returns the number of thread objects that are active.
  • threading.currentThread( ): Returns the number of thread objects in the caller's thread control.
  • threading.enumerate( ): Returns a list of all thread objects that are currently active.

The methods provided by the Thread class are as follows:
  • run( ): The run() method is the entry point for a thread.
  • start( ): The start() method starts a thread by calling the run method.
  • join([time]): The join() waits for threads to terminate.
  • isAlive( ): The isAlive() method checks whether a thread is still executing.
  • getName( ): The getName() method returns the name of a thread.
  • setName( ): The setName() method sets the name of a thread.


import threading

class X(threading.Thread):

    def run(self):
        for p in range(10):
            print(p)
class Y(threading.Thread):
    def run(self):
       for q in range(10,20):
            print(q) 
x1=X( )
x1.start( )
y1=Y( )
y1.start( )

suspending the execution of the thread temporary:
  • we can suspend the execution of the thread temporarily for some time by calling sleep( ) function of time module.

 import threading

import time
class x(threading.Thread):
    def run(self):
        time.sleep(1)
        for p in range(1,10):
            print(p)
class y(threading.Thread):
    def run(self):
        for p in range(11,20):
            print(p)
t1=x( )
t1.start( )
t2=y( )
t2.start( )
print("in main thread")

Note:
  • at the same time multiple threads access the same function, in this case deadlock is occurred.
  • to over come this problem,we are going to use the concept called synchronization.

synchronization:

  • the concept of avoiding the multiple threads to access the same functionality at a time is known as a synchronization.
  • Thread synchronization is defined as a mechanism which ensures that two or more concurrent processes or threads do not simultaneously execute some particular program segment known as critical section. Processes' access to critical section is controlled by using synchronization techniques. 
  • When one thread starts executing the critical section (serialized segment of the program) the other thread should wait until the first thread finishes.
  • If proper synchronization techniques are not applied, it may cause a race condition.
  • For example, suppose that there are three processes, namely 1, 2, and 3. All three of them are concurrently executing, and they need to share a common resource (critical section) as shown in Figure 1. Synchronization should be used here to avoid any conflicts for accessing this shared resource. Hence, when Process 1 and 2 both try to access that resource, it should be assigned to only one process at a time. If it is assigned to Process 1, the other process (Process 2) needs to wait until Process 1 frees that resource (as shown in Figure 2).

  • another synchronization requirement which needs to be considered is the order in which particular processes or threads should be executed. For example, we cannot board a plane until we buy a ticket. Similarly, we cannot check e-mails without validating our credentials (i.e., user name and password). In the same way, an ATM will not provide any service until we provide it with a correct PIN.
  • Other than mutual exclusion, synchronization also deals with the following:
    • deadlock: which occurs when many processes are waiting for a shared resource (critical section) which is being held by some other process. In this case, the processes just keep waiting and execute no further.
    • starvation:which occurs when a process is waiting to enter the critical section, but other processes monopolize the critical section, and the first process is forced to wait indefinitely.
    synchronization primitives, including semaphores, condition variables, events, and locks.
  • A semaphore is a very general synchronization primitive, and most other synchronization operations can be reduced to semaphore operations.

    Semaphores are in most cases too basic an abstraction to be used effectively. There are a few simple problems that are best solved with semaphores, but in general locks and condition variables are a much better abstraction.
  • A condition variable is an object used in combination with its associated lock to allow a thread to wait for some condition while it is inside a critical section. Only a thread holding the associated lock is allowed to use a condition variable associated with that lock. A condition variable has only one associated lock, but multiple condition variables may be associated with a single lock.
  • A lock is an object that can be held by at most one thread at a time. Only the thread that last acquired a lock is allowed to release that lock. Locks are useful for guarding critical sections. Keep in mind that multiple methods can be part of the same critical section.


  • we can implement the synchronization by using locking concept.
  • we can get the lock object by calling lock( ) function of threading module.
  • acquire the lock by using acquire( ) method.
  • release the lock by using release( ) method.
  • if any thread acquire the lock an any logic other threads cannot access same logic until lock is released by the other thread.



import threading

import time

class thread1(threading.Thread):
    def run(self):
        threadLock.acquire()
        f1("python")
        threadLock.release()
class thread2(threading.Thread):
    def run(self):
        threadLock.acquire()
        f1("hadoop")
        threadLock.release()
def f1(x):
    print("hello",x)
    time.sleep(5)
    print("world")
threadLock=threading.Lock()
t1=thread1()
t1.start()
t2=thread2()
t2.start()

suspending the execution of threads until execution of another thread:

  • we can suspend the execution of current thread until execution of specified threads are over by calling join( ) method Thread class.


import threading

import time
class thread1(threading.Thread):
    def run(self):
        threadLock.acquire()
        f1("python")
        threadLock.release()
class thread2(threading.Thread):
    def run(self):
        threadLock.acquire()
        f1("hadoop")
        threadLock.release()
def f1(x):
    print("hello",x)
    time.sleep(5)
    print("world")
threadLock=threading.Lock()
threads=[]
t1=thread1()
t1.start()
t2=thread2()
t2.start()
threads.append(t1)
threads.append(t2)
for t in threads:
    t.join()
print("end of the main thread")

Example:

import threading
import datetime

class ThreadClass(threading.Thread):
  def run(self):
    now = datetime.datetime.now()
    print "%s says Hello World at time: %s" %(self.getName(), now)

for i in range(2):
  t = ThreadClass()

  t.start()


Using queues with threads:

  • threading can be complicated when threads need to share data or resources. 
  • The threading module does provide many synchronization primitives, including semaphores, condition variables, events, and locks.
  • While these options exist, it is considered a best practice to instead concentrate on using queues. 
  • Queues are much easier to deal with, and make threaded programming considerably safer, as they effectively funnel all access to a resource to a single thread, and allow a cleaner and more readable design pattern.


Basic FIFO Queue:



  • The Queue class implements a basic first-in, first-out container. 
  • Elements are added to one “end” of the sequence using put( ), and removed from the other end using get( ).

import Queue

q = Queue.Queue( )

for i in range(5):
    q.put(i)

while not q.empty( ):
    print q.get( )


LIFO Queue


  • The item most recently put( ) into the queue is removed by  get( ).

import Queue

q = Queue.LifoQueue()

for i in range(5):
    q.put(i)

while not q.empty( ):
    print q.get( )


Priority Queue:

import Queue

class Job(object):
    def __init__(self, priority, description):
        self.priority = priority
        self.description = description
        print 'New job:', description
        return
    def __cmp__(self, other):
        return cmp(self.priority, other.priority)

q = Queue.PriorityQueue()

q.put( Job(3, 'Mid-level job') )
q.put( Job(10, 'Low-level job') )
q.put( Job(1, 'Important job') )

while not q.empty():
    next_job = q.get()
    print 'Processing job:', next_job.description



Example:

from Queue import Queue
from threading import Thread

def do_stuff(q):
  while True:
    print q.get()
    q.task_done()

q = Queue(maxsize=0)
num_threads = 10

for i in range(num_threads):
  worker = Thread(target=do_stuff, args=(q,))
  worker.setDaemon(True)
  worker.start()

for x in range(100):
  q.put(x)

q.join()


Regular Expressions

Regular Expressions:

  • regular expression is a special sequence of characters that helps you match or find other strings or sets of strings, using a specialized syntax held in a pattern. 

Regular Expressions are used to

  • Extracting the required data from the given data.
  • To perform data validations.
  • To develop the URL patterns in the web applications.


  • In Regular Expressions we use some special characters to define the patterns.
  • After defining the pattern we can extract that pattern matching data from the given data by using pre-defined functions of re module.
  • re is a inbuilt module of the python

special characters:



1) *  => it matches zero or more occurrences of preceding 

character.


*   ---->ab*c

ac

abc

abbc

abbbbbc


2) + => it matches one or more occurrences of preceding character.

+ -----> ab+c

ac   #invalid

abc

abbc

abbbbbc

3) ? => it matches zero or one occurrence of preceding character.

? ---->ab?c

Ac

Abc

Abbc #invalid

Perl,pearl => pea?rl

Color,colour=>colou?r

4) . => it matches any single character.

. ------> a.c

agc

a5c

a$c

a c

abcd #invalid

5)[ ] => it matches any single character in the given list.

[xyz…] ----->b[aeiou]d

bad

bed

bid

bod

bud

b8d #invalid

bpd #invalid

6) [^] => it matches any single character other than in the give list.

[^xyz…]------>b[^aeiou]d

Bad #invalid

Bed #invalid

Bid #invalid

Bod #invalid

Bud #invalid

B8d

Bpd

7) [-]  => it matches any single character in the given range.

z[a-e]y

xay

xby

xcy

xdy

xey

xfy #invalid

xpy #invalid

[0-9] --->any single digit

[a-z] --->any one lowercase alphabet

[A-Z] --->any one uppercase alphabet

[a-zA-Z] --->any one alphabet

[a-zA-Z0-9_] --->any one alphanumeric

[^0-9] --->any single non digit

[^a-z] --->any one non lowercase alphabet

[^A-Z] --->any one non uppercase alphabet

[^a-zA-Z] --->any one non alphabet

[^a-zA-Z0-9_] --->any one non alphanumeric(special characters)

8) ( | )  =>match any one string in the list.

(java|hadoop|python)

9) {m} =>it matches exact occurrence of preceding character.

ab{3}c

abbc #invalid

abbbc

abbbbbc #invalid

10) {m,n}  => it matches min m occurrences and max n 

occurrences of its preceding character.

ab{3,5}c

abbc #invalid

abbbc

abbbbc

abbbbbc

abbbbbbc #invalid

11) {m,}  => it matches min m occurrences and max no limit of its

 preceding character

ab{3,}c

abbc #

abbbc

abbbbbbbc


12) ^  => start of the line

^perl

^[abc]

^[^abc]


13) $   => end of the line

Perl$

[0-9]$


14) \d or [0-9]  => any single digit.

[0-9][0-9][0-9][0-9] or [0-9]{4} or \d\d\d\d or \d{4}


15) \D or [^0-9]  => any single non digit


16) \w or [a-zA-Z0-9_]  => any alphanumeric


17) \W or [^a-zA-Z0-9_]  => any non alphanumeric or special character


18) \s  => ’ ‘,’\t’,’\n’


19) \b =>word boundary


  • To avoid any confusion while dealing with regular expressions, we would use Raw Strings as r'expression'

 Functions in re module:


match( ): Match a regular expression pattern to the 

beginning of a string.

re.match(pattern, string, flags=0)


search( ): Search a string for the presence of a pattern.

re.search(pattern, string, flags=0)


sub( ): Substitute occurrences of a pattern found in a 

string.

re.sub(pattern, repl, string, max=0)


subn( ): Same as sub, but also return the number of 

substitutions made.


split( ): Split a string by the occurrences of a pattern.

re.split(pattern, string)


findall( ): Find all occurrences of a pattern in a string.

re.findall(pattern, string)


finditer( ): Return an iterator yielding a match object 

for each match.

re.finditer(pattern, string)


compile( ):  Compile a pattern into a RegexObject.

re.compile(pattern)

       

Matching Versus Searching


match checks for a match only at the beginning of the string, while search checks for a match anywhere in the string .

Search and Replace


One of the most important re methods that use regular expressions is sub.
re.sub(pattern, repl, string, max=0)
This method replaces all occurrences of the RE pattern in string with repl, substituting all occurrences unless max provided. This method returns modified string.
greedy matching:
The notion that the "+" and "*" characters in a regular expression expand outward to match the largest possible string. 
wild card:
A special character that matches any character. 
In regular expressions the wild card character is the period character.

character Classes:


In many cases, rather than matching one particular character we want to match any one of a set of characters. 

This can be achieved by using a character class—one or more characters enclosed in square brackets.

Symbol
Meaning
.
Matches any character except newline, any character at all with the re.DOTALL flag, or inside a character class matches a literal period.
\d
Matches a Unicode digit, or [0-9] with the re.ASCII flag.
\D
Matches a Unicode nondigit, or [^0-9] with the re.ASCII flag.
\s
Matches a Unicode whitespace, or [ \t\n\r\f\v] with the re.ASCII flag.
\S
Matches a Unicode non-whitespace, or [^ \t\n\r\f\v] with the re.ASCII flag.
\w
Matches a Unicode "word" character, or [a-zA-Z0-9_] with the re.ASCII flag.
\W
Matches a Unicode non-"word" character, or [^a-zA-Z0-9_] with the re.ASCII flag.

Quantifiers

A quantifier has the form {m,n} where m and n are the minimum and maximum times the expression to which the quantifier applies must match.
Syntax
Meaning
e? or e{0,1}
Greedily match zero occurrences or one occurrence of expression e.
e?? or e{0,1}?
Nongreedily match zero occurrences or one occurrence of expression e.
e+ or e{1,}
Greedily match one or more occurrences of expression e.
e+? or e{1,}?
Nongreedily match one or more occurrences of expression e.
e* or e{0,}
Greedily match zero or more occurrences of expression e.
e*? or e{0,}?
Nongreedily match zero or more occurrences of expression e.
e{m}
Match exactly m occurrences of expression e.
e{m,}
Greedily match at least m occurrences of expression e.
e{m,}?
Nongreedily match at least m occurrences of expression e.
e{,n}
Greedily match at most n occurrences of expression e.
e{,n}?
Nongreedily match at most n occurrences of expression e.
e{m,n}
Greedily match at least m and at most n occurrences of expression e.
e{m,n}?
Nongreedily match at least m and at most n occurrences of expression e.
Regular Expression Basics
.Any character except newline
aThe character a
abThe string ab
a|ba or b
a*0 or more a's
\Escapes a special character
Regular Expression Character Classes
[ab-d]One character of: a, b, c, d
[^ab-d]One character except: a, b, c, d
[\b]Backspace character
\dOne digit
\DOne non-digit
\sOne whitespace
\SOne non-whitespace
\wOne word character
\WOne non-word character
Regular Expression Flags
iIgnore case
m^ and $ match start and end of line
s. matches newline as well
xAllow spaces and comments
LLocale character classes
uUnicode character classes
(?iLmsux)Set flags within regex
Regular Expression Quantifiers
*0 or more
+1 or more
?0 or 1
{2}Exactly 2
{2, 5}Between 2 and 5
{2,}2 or more
(,5}Up to 5
Regular Expression Assertions
^Start of string
\AStart of string, ignores m flag
$End of string
\ZEnd of string, ignores m flag
\bWord boundary
\BNon-word boundary
(?=...)Positive lookahead
(?!...)Negative lookahead
(?<=...)Positive lookbehind
(?<!...)Negative lookbehind
(?()|)Conditional
Regular Expression Special Characters
\nNewline
\rCarriage return
\tTab
\YYYOctal character YYY
\xYYHexadecimal character YY
Regular Expression Groups
(...)Capturing group
(?P<Y>...)Capturing group named Y
(?:...)Non-capturing group
\YMatch the Y'th captured group
(?P=Y)Match the named group Y
(?#...)Comment
Regular Expression Replacement
\g<0>Insert entire match
\g<Y>Insert match Y (name or number)
\YInsert group numbered Y
Splitting Strings on Any of Multiple Delimiters
The split () method of string objects is really meant for very simple cases, and does not allow for multiple delimiters.
In cases when you need a bit more flexibility, use the re.split () method:
message="hai,siva krishna;hw r u"
import re
re.split(r'[,;\s]\s*',message)
['hai', 'siva', 'krishna', 'hw', 'r', 'u']



import re


regex=r"[a-zA-Z]+  \d+"


matches=re.findall(regex,"June 24,August 9,Oct 13,Dec")

for match in matches:

    print("Full match: ",match)



import re

regex=r"([a-zA-Z]+)  \d+"

matches=re.findall(regex,"June 24,August 9,Oct 13,Dec")

for match in matches:

    print("Full match: ",match)



import re

regex=r"[a-zA-Z]+ \d+"

matches1=re.findall(regex,"June 24,August 9,Oct 13,Dec")

matches2=re.finditer(regex,"June 24,August 9,Oct 13,Dec")

print(type(matches1))

print(type(matches2))

for match in matches1:

    print(match)

for match in matches2:

    print(match.start( ),match.end( ))



import re

regex=r"([a-zA-Z]+) (\d+)"

print(re.sub(regex,r"\2 of \1","June 24,August 9,Oct 13,Dec"))



import re

regex=r"([a-zA-Z]+) (\d+)"

x=re.sub(regex,r"\2 of \1","June 24,August 9,Oct 13,Dec")

regex1="\d+ [a-zA-Z]+ ([a-zA-Z]+)"

matches=re.findall(regex1,x)

for match in matches:

    print(match)



import re

regex=re.compile(r"(\w+) World")

result=regex.search("Hello World is the easiest")

print(result)

if result:

    print(result.start(),result.end())



import re

regex=re.compile(r"(\w+) World")

x=regex.findall("Hello World.Hai World")

for result in x:

    print(result)


import re

regex=re.compile(r"(\w+) World")

print(regex.sub(r"\1 Earth","Hello World"))


import re

line = "Cats are smarter than dogs"

matchObj = re.match( r'(.*) are (.*?) .*', line, re.M|re.I)

if matchObj:

   print "matchObj.group( ) : ", matchObj.group( )

   print "matchObj.group(1) : ", matchObj.group(1)

   print "matchObj.group(2) : ", matchObj.group(2)

else:

   print "No match!!"


import re

line = "Cats are smarter than dogs";

searchObj = re.search( r'(.*) are (.*?) .*', line, re.M|re.I)

if searchObj:

   print "searchObj.group( ) : ", searchObj.group( )

   print "searchObj.group(1) : ", searchObj.group(1)

   print "searchObj.group(2) : ", searchObj.group(2)

else:

   print "Nothing found!!"



import re

line = "Cats are smarter than dogs";

matchObj = re.match( r'dogs', line, re.M|re.I)

if matchObj:

   print "match --> matchObj.group( ) : ", matchObj.group( )

else:

   print "No match!!"

searchObj = re.search( r'dogs', line, re.M|re.I)

if searchObj:

   print "search --> searchObj.group( ) : ", searchObj.group( )

else:

   print "Nothing found!!"