Decorators
Definition: A decorator is essentially a function (that decorates other functions) to add extra functionality to other functions.
Principles:
- Cannot modify the source code of the decorated function.
- Cannot change the way the decorated function is called.
Prerequisites for Implementing Decorators:
- Functions are 'variables'.
- Higher-order functions:
- a. Pass a function name as an argument to another function (to add functionality with out modifying the decorated function).
- b. Return a function name (with out changing the calling method).
- Nested functions.
Higher-order functions + Nested functions = Decorators
Simple Higher-order Function
import time
def bar():
time.sleep(3)
print('in the bar')
def test2(func):
print(func)
return func
bar = test2(bar)
bar() # run bar
Simple Nested Function
def foo():
print('in the foo')
def bar():
print('in the bar')
bar()
foo()
Decorator = Higher-order Function + Nested Functon
import time
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
func()
stop_time = time.time()
print('the func run time is %s' % (stop_time - start_time))
return wrapper
@timer
def test1():
time.sleep(3)
print('in the test1')
test1()
Modified Decorator (Arbitrary Arguments)
import time
def timer(func): # timer(test1) func=test1
def deco(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs) # run test1()
stop_time = time.time()
print("the func run time is %s" % (stop_time - start_time))
return deco
@timer # test1 = timer(test1)
def test1():
time.sleep(1)
print('in the test1')
test1()
Modified Decorator (Arbitrary Arguments v2)
import time
def timer(func): # timer(test1) func=test1
def deco(*args, **kwargs):
start_time = time.time()
r = func(*args, **kwargs) # run test1()
stop_time = time.time()
print("the func run time is %s" % (stop_time - start_time))
return r
return deco
@timer # test1 = timer(test1)
def test1():
time.sleep(1)
print('in the test1')
return "you are a BIG B"
@timer # test2 = timer(test2) = deco test2(name) = deco(name)
def test2(name, age):
print("test2:", name, age)
a = test1()
print(a)
test2("abc", 123)
Output:
in the test1
the func run time is 1.0000011920928955
you are a BIG B
test2: abc 123
the func run time is 0.0
Advanced Decorator
import time
user, passwd = 'lance', '123456'
def auth(auth_type):
print("auth func:", auth_type)
def outer_wrapper(func):
def wrapper(*args, **kwargs):
print("wrapper func args:", *args, **kwargs)
if auth_type == "local":
username = input("Username:").strip()
password = input("Password:").strip()
if user == username and passwd == password:
print("\033[32;1mUser has passed authentication\033[0m")
res = func(*args, **kwargs) # from home
print("---after authentication")
return res
else:
exit("\033[31;1mInvalid username or password\033[0m")
elif auth_type == "ldap":
print("No ldap support for now...")
return wrapper
return outer_wrapper
@auth(auth_type="local")
def home():
print("welcome to home page")
return "from home"
print(home())
Output:
auth func: local
wrapper func args:
Username: lance
Password: 123456
User has passed authentication
welcome to home page
---after authentication
from home
Generators
Omitted.
Iterators
Omitted.