Lets look at decorators again. They're related to what we call "function composition" in that the decorator "eats" what's defined just below it, and returns a proxy with the same name, likewise a callable.
Here's a decorator that tells the identity function under it what letter to tack on (concatenate) to the end of string s, the argument.
The decorator itself takes an argument. The callable it then returns, the adder function, is ready to do the work of finally "eating" ident and extending what it does by the one letter).
In [1]:
def plus(char):
"""
returns a prepped adder to eat the target, and to
build a little lambda that does the job.
"""
def adder(f):
return lambda s: f(s) + char
return adder
@plus('R')
def ident(s):
return s
ident('X') # do the job!
Out[1]:
Now let's stack up some decorators, showing how each swallows the result below. Work from the bottom up...
In [2]:
# append from the bottom up, successive wrappings
@plus('W')
@plus('A')
@plus('R')
def ident(s):
return s
print("ident('X') :", ident('X'))
print("ident('WAR'):", ident('WAR'))
Now let's bring Compose into the mix, a decorator class makes our proxies able to compose with one another by means of multiplication. Even powering has been implemented. We're free to make our target functions composable, in addition to controlling what letters to add.
In [3]:
class Compose:
"""
make function composible with multiply
also make self powerable e.g. f ** 3 == f * f * f
From: https://repl.it/@kurner
"""
def __init__(self, f):
self.func = f
def __mul__(self, other):
return Compose(lambda x: self(other(x)))
def __pow__(self, n):
if n == 0:
return Compose(lambda x: x) # identity function
if n == 1:
return self
if n > 1:
me = self
for _ in range(n-1):
me *= self # me times me times me...
return me
def __call__(self, x): # callable instances
return self.func(x)
In [4]:
@Compose
@plus('W')
@plus('A')
@plus('R')
def ident(s):
return s
H = ident ** 3
H('X')
Out[4]:
In [5]:
@Compose
@plus('T')
@plus('Y')
@plus('P')
def F(s):
return s
@Compose
@plus('N')
@plus('O')
@plus('H')
def G(s):
return s
H = F * G * G * F
H('')
Out[5]:
In [6]:
@plus('EXPERIMENT!')
@plus('ANOTHER ')
@plus('DO ')
@plus('LETS ')
def ident(s): return s
ident('')
Out[6]: