def inc(x):
return x + 1
def dec(x):
return x - 1
def operate(func, x):
return func(x)
>>> operate(inc, 6)
7
>>> operate(dec, 6)
5
def is_called():
def is_returned():
print("Hello")
return is_returned()
>>> is_called()
Hello
Also is_returned
object can be returned from is_called
instead of calling it:
def is_called():
def is_returned():
print("Hello")
return is_returned # WITHOUT parenthesis
>>> new = is_called()
>>> new()
Hello
nonlocal
is used to declare that a variable inside a nested function is not local to it, meaning it lies in the outer inclosing function. If we need to modify the value of a non-local variable inside a nested function, then we must declare it with nonlocal
. Otherwise a local variable with that name is created inside the nested function.
def outer_function():
a = 5
def inner_function():
nonlocal a
a = 10
print("Inner function: ",a)
inner_function()
print("Outer function: ",a)
>>> outer_function()
Inner function: 10
Outer function: 10
returning a nested fuction object
nonlocal variable
The method of binding data to a function without actually passing them as parameters is called closure. It is a function object that remembers values in enclosing scopes even if they are not present in memory.
def logger(func):
def log_func(*args):
print('Running "{}" with arguments {}'.format(func.__name__, args))
print(func(*args))
return log_func
def inc(x):
return x + 1
def dec(x):
return x - 1
>>> inc_logger = logger(inc)
>>> dec_logger = logger(dec)
>>> inc_logger(5)
Running "inc" with arguments (5,)
6
>>> dec_logger(5)
Running "dec" with arguments (5,)
4
closures
Functions and methods are called callable as they can be called.
In fact, any object which implements the special method __call__()
is termed callable. So, in the most basic sense, a decorator is a callable that returns a callable.
Basically, a decorator takes in a function, adds some functionality and returns it.
Add some syntax sugar in logger example above to complete its transformation into a decorator:
@logger
def inc(x):
return x + 1
@logger
def dec(x):
return x - 1
>>> inc(5)
Running "inc" with arguments (5,)
6
>>> dec(5)
Running "dec" with arguments (5,)
4
With defined parameters:
def smart_divide(func):
def inner(a,b):
print("I am going to divide {} and {}".format(a, b))
if b == 0:
print("Can't divide")
return
return func(a,b)
return inner
@smart_divide
def divide(a,b):
return a/b
>>> divide(2,5)
I am going to divide 2 and 5
0.4
>>> divide(2,0)
I am going to divide 2 and 0
Can't divide
Chaining Decorators
def star(func):
def inner(*args, **kw):
print("*" * 10)
func*args, **kw()
print("*" * 10)
return inner
def percent(func):
def inner(*args, **kw):
print("%" * 10)
func*args, **kw()
print("%" * 10)
return inner
@star
@percent
>>> def printer(msg):
... print(msg)
>>> printer("Hello")
**********
%%%%%%%%%%
Hello
%%%%%%%%%%
**********