Function Scope Review
Remember that Python functions create a new scope, meaning the function has its own namespace to find variable names when they are mentioned within the function. We can check for local variables and global variables with the locals() and globals() functions.
s = 'Global Variable'
def check_for_locals():
a=5
b="Ram"
print("globals() :\n{}\n".format(globals()))
print("globals()['s'] :\n{}\n".format(globals()['s']))
print("locals() :\n{}\n".format(locals()))
print("globals().keys() :\n{}".format(globals().keys()))
print("locals().keys() :\n{}".format(locals().keys()))
check_for_locals()
# Output :
globals() :
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None,
'__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 's': 'Global Variable',
'check_for_locals': <function check_for_locals at 0x7f10c19d2d30>}
globals()['s'] :
Global Variable
locals() :
{'a': 5, 'b': 'Ram'}
globals().keys() :
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', 's', 'check_for_locals'])
locals().keys() :
dict_keys(['a', 'b'])
Passing Function into other functions.
In Python everything is an object. That means functions are objects which can be assigned labels and passed into other functions.
def sayHi(name="Sam"):
return "Hello "+name
print(sayHi()) # Hello Sam
hey = sayHi
print(hey()) # Hello Sam
Even though we deleted the name sayHi, the name greet still points to our original function object. It is important to know that functions are objects that can be passed to other objects!
del sayHi
print(hey()) # Hello Sam
print(sayHi()) # Traceback (most recent call last): File "<string>", line 7, in <module> NameError: name 'sayHi' is not defined
Nested Functions : Functions within functions
def sayHi(name='Sam'):
print('Inside sayHi() function')
def greet():
return '\tInside greet() function'
def welcome():
return "\tInside welcome() function"
print(greet())
print(welcome())
print("Now we back inside the hello() function")
sayHi()
# Output :
Inside sayHi() function
Inside greet() function
Inside welcome() function
Now we back inside the hello() function
Due to scope, the greet() and welcome() function is not defined outside of the sayHi() function.
print(greet())
# Output :
Traceback (most recent call last):
File "", line 15, in
NameError: name 'greet' is not defined
Returning Functions :
def sayHi(name='Sam'):
print('Inside sayHi() function')
def greet():
return '\tInside greet() function'
def welcome():
return "\tInside welcome() function"
if name=='Sam':
return greet # Without ()
else:
return welcome
In the if/else clause we are returning greet and welcome, not greet() and welcome().
This is because when we put a pair of parentheses after the function, the function gets executed; whereas if we don’t put parentheses after it, then it can be passed around and can be assigned to other variables without executing it.
x = sayHi()
print(x)
print(x())
# Output :
Inside sayHi() function
< function sayHi..greet at 0x7f3238682dc0 >
Inside greet() function
Functions as Arguments
def sayHi():
return 'Hi There!!'
def sayHello(func):
print('Other code would go here')
print(func())
sayHello(sayHi)
# Output :
Other code would go here
Hi There!!
Decorator in Python :
Decorators can be thought of as functions which modify the functionality of another function.
Decorators are like gift wrappers.If we want to extend the behavior of a function but don’t want to modify it permanently, we can wrap a decorator on it.
def new_decorator(func):
def wrap_func():
print("Some lines of code ...before executing the function...")
func()
print("Some lines of code ...after executing the function...")
return wrap_func
def func_needs_decorator():
print("This function is in need of a Decorator")
func_needs_decorator()
# Output :
This function is in need of a Decorator
# Reassign func_needs_decorator
func_needs_decorator = new_decorator(func_needs_decorator)
func_needs_decorator()
# Output :
Some lines of code ...before executing the function...
This function is in need of a Decorator
Some lines of code ...after executing the function...
A decorator simply wrapped the function and modified its behavior. Now let's understand how we can rewrite this code using the @ symbol, which is what Python uses for Decorators:
@new_decorator
def func_needs_decorator():
print("This function is in need of a Decorator")
func_needs_decorator()
# Output :
Some lines of code ...before executing the function...
This function is in need of a Decorator
Some lines of code ...after executing the function...