Showing posts with label Python_closure. Show all posts
Showing posts with label Python_closure. Show all posts

Jun 2, 2019

Python decorator to find out execution time taken by a function

import functools
import time

def timer(f): # without functools.wraps
    def timer_wrap(*args, **kwargs):
        """timer_wrap documentation """
        print('inside timer_wrap decorator')
        start_time = time.time()
        f(*args, **kwargs)
        end_time = time.time()
        print('Total Time Taken by function %s is : %4f secs' % (f.__name__, end_time - start_time))
        # f.__name__ gives function name
    return timer_wrap

def timer_wrap_with_functools(f): # with functools.wraps
    @functools.wraps(f)
    def timer_wrap(*args, **kwargs):
        """timer_wrap_with_functools documentation """
        print('inside timer_wrap_with_functools decorator')
        start_time = time.time()
        f(*args, **kwargs)
        end_time = time.time()
        print('Total Time Taken by function %s is : %4f secs' % (f.__name__, end_time - start_time))
    return timer_wrap


@timer
def test_timer_func(num_times):
    """test_timer_func documentation """
    total_sum = 0
    for _ in range(num_times):
        total_sum += sum([i ** 2 for i in range(1000)])
    print('Total Sum: %f ' % total_sum)

@timer_wrap_with_functools
def test_timer_func_functools(num_times):
    """test_timer_func_functools documentation """
    total_sum = 0
    for _ in range(num_times):
        total_sum += sum([i ** 2 for i in range(1000)])
    print('Total Sum: %f ' % total_sum)


if __name__ == '__main__':
    print('------------------------------------')
    test_timer_func(200)
    print(test_timer_func.__name__)  #gives wrapper name
    print(test_timer_func.__doc__)   #gives wrapper name
    print('------------------------------------')
    test_timer_func_functools(200)
    print(test_timer_func_functools.__name__) #gives function name
    print(test_timer_func_functools.__doc__)  #gives function name
    print('------------------------------------')


# Output:
------------------------------------
inside timer_wrap decorator
Total Sum: 66566700000.000000
Total Time Taken by function test_timer_func is : 0.205079 secs
timer_wrap
timer_wrap documentation
------------------------------------
inside timer_wrap_with_functools decorator
Total Sum: 66566700000.000000
Total Time Taken by function test_timer_func_functools is : 0.188637 secs
test_timer_func_functools
test_timer_func_functools documentation
------------------------------------




Jan 21, 2019

Python Local Vs Global

#########################
#   Global Scope Vs Enclosing Scope Vs Local Scope 
#   LEGB rule
#   Local(L): Defined inside function/class
#   Enclosed(E): Defined inside enclosing functions(Nested function concept)
#   Global(G): Defined at the uppermost level
#   Built-in(B): Reserved names in Python builtin modules
#########################

message = 'global'

def enclosing():
    message = 'enclosing'
    def local():
        message = 'local'
    print('enclosing message: ', message)   # enclosing
    local()
    print('enclosing message: ', message)   # enclosing


def enclosing_nonlocal():
    message = 'enclosing'
    def local():
        nonlocal message    # This refers to the above message in enclosing scope, not in global scope
        message = 'local'
    print('enclosing message: ', message)   # enclosing
    local()
    print('enclosing message: ', message)   local


def enclosing_global():
    message = 'enclosing'
    def local():
        global message
        message = 'local'  # Here you are updating message in global scope, not in enclosing scope
    print('enclosing message: ', message)   enclosing
    local()
    print('enclosing message: ', message)    enclosing 


if __name__ == '__main__':
    print('------------------------------------')
    print('global message: ', message)
    enclosing()
    print('global message: ', message)
    print('----------------NONLOCAL------------------')
    print('global message: ', message)
    enclosing_nonlocal()
    print('global message: ', message)
    print('----------------GLOBAL--------------------')
    print('global message: ', message)
    enclosing_global()
    print('global message: ', message)
    print('------------------------------------')

"""
Output:

------------------------------------
global message:  global
enclosing message:  enclosing
enclosing message:  enclosing
global message:  global
----------------NONLOCAL------------------
global message:  global
enclosing message:  enclosing
enclosing message:  local
global message:  global
----------------GLOBAL--------------------
global message:  global
enclosing message:  enclosing
enclosing message:  enclosing
global message:  local
------------------------------------

"""

Python Closure

#########################
# Closure in Python
# A nested function references a value in its enclosing scope.
# We should have a nested function (function within a function).
# The nested function should refer to a value defined in the enclosing function.
# The enclosing function must return the nested function.
# Closures are used as callback functions, this helps in data hiding. This helps to reduce the use of global variables.
# When we have few functions in our code, closures are helpful. But if we have many functions, then we may go for a class
#########################

# Nested Function
# inner_function() can easily be accessed inside the outer_function body but not outside of it’s body.
# Hence, inner_function() is treated as nested Function which uses text as non-local variable.
def outer_function(text):
    text = text
    def inner_function():
        print(text)
    inner_function()


# A closure — unlike a plain function as above — allows the function to access those enclosed captured variables through
# the closure’s copies of their values or references, even when the function is invoked outside their scope.
def closure_outer_function(text):
    text = text
    def closure_inner_function():
        print(text)
    return closure_inner_function  # without parentheses() / callback function


def enclosed_function(x):
    print('In enclosed_function: ' + str(x))

    def nested_function(y):
        print('###')
        print('In nested_function x : ' + str(x))
        print('In nested_function y : ' + str(y))

    return nested_function    # without parentheses() / callback function


if __name__ == '__main__':
    print('------------------------------------------------------')
    outer_function('Hi Nested Function')
    print('------------------------------------------------------')
    func_obj = closure_outer_function('Hi Closure')
    func_obj()
    print('------------------------------------------------------')
    func_obj = enclosed_function(100)
    print('After calling enclosed_function')
    func_obj(111)
    print('------------------------------------------------------')


"""
------------------------------------------------------
Hi Nested Function
------------------------------------------------------
Hi Closure
------------------------------------------------------
In enclosed_function: 100
After calling enclosed_function
###
In nested_function x : 100
In nested_function y : 111
------------------------------------------------------
"""