Functionals serve a useful purpose for when certain variables are evaluated.
Coming from an outsider, here's a series of more friendly examples:
from functools import partial
sum = lambda x, y: x + y # sum(x, y) == x + y
n = 2
normalSum = lambda x: sum(x, n) # normalSum(x) == sum(x, y=n)
partialSum = partial(sum, y = n) # partialSum(sum(y=n)) == sum(x, 2)
print(normalSum(2), partialSum(2)) # 4 4
n = 6
print(normalSum(2), partialSum(2)) # 8 4
Notice how the partial holds the value of whatever was n at the time.
...
n = 2
partialSumOrig = partial(sum, y = n) # partialSumOrig(sum(y=n)) == sum(x, 2)
n = 6
partialSumNew = partial(sum, y = n) # partialSumNew(sum(y=n)) == sum(x, 6)
print(partialSumOrig(2), partialSumNew(2)) # 4 8
Extra example showing how arguments are passed into nested lambdas:
...
n = 8
partialSumOrig = partial(sum, y = n) # partialSumOrig(sum(y=n)) == sum(x, 8)
partialSumNew = partial(sum, n) # partialSumNew(sum(n)) == sum(8, y)
print(partialSumOrig(2)) # 10 # partialSumOrig(sum(2, 8)) == sum(2, 8)
print(partialSumNew(2)) # 10 # partialSumNew(sum(8, 2)) == sum(8, 2)
One last example showing how arguments are passed in partials:
...
n = 2
m = 2
partialSumSilly = partial(sum, n, m) # partialSumSilly(sum(n, m)) == sum(2, 2)
print(partialSumSilly()) # 4
The big takeaway is that:
normalSum() behaves like a late binding, where n is evaluated when ran.
partialSum() behaves like an early binding, where n is evaluated when defined.
Note: In reality nearly everything is a late binding in cpython due to its interpreted nature.