
# The functools is a Python module for higher-order functions: 
#
#       functions that act on or return other functions. 
#
# In general, any callable object can be treated as a function 
# for the purposes of this module.

# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
# functools.partial(func, /, *args, **keywords)
#
# Return a new partial object which when called will behave like 
# func called with the **positional arguments** args and **keyword arguments** keywords. 
#
# If more (non-keyword) arguments are supplied to the call, 
# they are **appended** to args. 
# If additional keyword arguments are supplied, they extend and 
# override keywords. 
#
# Roughly equivalent to:
#
#   def partial(func, /, *args, **keywords):
#       def newfunc(*fargs, **fkeywords):
#           newkeywords = {**keywords, **fkeywords}
#           return func(*args, *fargs, **newkeywords)
#       newfunc.func = func
#       newfunc.args = args
#       newfunc.keywords = keywords
#       return newfunc
# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

# #####################################################
# How to use functools.partial
# #####################################################

# When passing functions around, sometimes we’ll want to partially apply (or curry)
# functions to create new functions. As a simple example, imagine that we have a function
# of two variables:

# Original general function:

def exp(base, power):
    return base ** power

# A specific version of power() that uses base = 2

def two_to_the(power):
    return exp(2, power)

print( two_to_the(3) )		# Calls: exp(2,3)

# A different approach to define two_to_the() is to use functools.partial:

from functools import partial
two_to_the = partial(exp, 2)

# Now this is the same:

print( two_to_the(3) )  	# Calls: exp(2,3)

# ##############################################################################
# You can also use partial to fill in later arguments by specify their names:
# ##############################################################################

def exp(base, power):
    return base ** power

square_of = partial(exp, power=2)	# square_of(x) = exp(x, power=2)

print(square_of(3))		# Calls: exp(3, power=2)

