About_Decorators


Decorators

I use UFO as a decorator not because I want or need people to believe in UFOs, but because the science fiction idea of being abducted is you stay the same but for something lasting the UFO did to you.

In the case of decorator syntax that's useful because to "decorate" ("abduct") is to

  • feed a function to a callable (the decorator), and
  • keep that function's name for whatever gets returned

Since function type objects are just objects with a __dict__, we're free to apply arbitrary attributes to them. Lets have the UFO decorator decorate any function with a new attribute named 'abducted'.


In [6]:
def UFO(f):
    setattr(f, 'abducted', True) # f.abducted = True same thing
    return f

@UFO
def addS(s):
    return s + "S"

@UFO
def addX(s):
    return s + "X"

In [4]:
hasattr(addX, 'abducted')


Out[4]:
True

In [5]:
if hasattr(addS, 'abducted'):
    print("The value of abducted for addS is:", addS.abducted)


The value of abducted for addS is: True

In the example below, the Composer class "decorates" the two following functions, meaning the Composer instances become the new proxies for the functions they swallowed. The original functions are still on tap, through __call__.

Furthermore, when two such Composer types are multiplied, their internal functions get composed together, into a new internalized function.


In [7]:
class Composer:
    
    def __init__(self, f):
        self.func = f
        
    def __call__(self, s):
        return self.func(s)
    
    def __mul__(self, other):
        def new(s):
            return self(other(s))
        return Composer(new)

@Composer
def F(x):
    return x * x

@Composer
def G(x):
    return x + 2

Below is simple composition of functions. This is valid Python even if the Composer decorator is left out, i.e. function type objects would normally have no problem composing with one another in this way.

To compose F and G means going F(G(x)) for some x.


In [8]:
F(G(F(F(F(G(10))))))


Out[8]:
184884260614963204

Thanks to Compose, the "class decorator" (a decorator that happens to be a class), our F and G are actually Compose type objects, so have this additional ability to compose into other Compose type objects. We don't need an argument until we call the final H.


In [9]:
H = F*G*F*F*F*G  # the functions themselves may be multiplied
H(10)


Out[9]:
184884260614963204

In [ ]: