[Python-Dev] Re: PEP 318: Let's propose some useful built-in decorators

Isaac Freeman ishnigarrab at earthlink.net
Sat Apr 3 19:00:38 EST 2004


Here are a few ideas (including a meta-decoration! :)

Of course, I'm biased to first-before-colon style. ;)

--- decorators.py ---

import inspect
class decorator:
    """ An obscenely gratuitous example of decorator usage. Meta-decoration?
        "One mans use is another man's abuse"
    """

    class decoration:
        """ The object that represents the decoration itself. """
        def __init__(decor, func, **state):
            self.func = func
            self.state = {'func': func.__name__}

        def __call__(self, *args, **kwds):
            """ Called when the function that is decorated by this 
decoration is
                called. (whew)
            """
            self.state.update(inspect.getargvalues(sys._getframe(1))[3]) 
# better way?           
            return self.decor(self.func, self.state, *args, **kwds)

        def options(self, opts=None, **kwd_opts):
            self.state.update(opts or kwd_opts)
            return self

    def __init__(self, decor):
        self.decor = decor

    def __call__(self, func):
        """ Called when the decorator is called to decorate a desired 
function. """
        init = self.init
        init.update(kwds)
        return self.decoration(decor, func)

    def fromstate(cls, state=None, **kwd_state) [classmethod]:
        """ Make a decoration with a specified initial state """
        return lambda fn: cls(fn, state or kwd_state)

# or...

# Smaller, but can be confusing. (But I guess the whole
# meta-function-generator thing is to begin with!)
def decorator2(decor=None, **state):
    """ Decoration factory. """

    def decoration2(func=None, **opts):
        """ Decorating function wrapper. """
        state.update(opts)
        state['func'] = func.__name__
       
        def callit(*args, **kwds):
            """ Function replacement. """
            state.update(inspect.getargvalues(sys._getframe(1))[3]) # 
better way?           
            decor(func, state, *args, **kwds)

        if func:
            return callit
        else:
            return lambda fn: decoration2(fn, **opts)

    if decor:
        return decoration
    else:
        return lambda dec: decorator2(dec, state)


def memoized(func, state, *args, **kwds) [decorator]:
    """ Function wrapper to cache the results of previously
        evaluated arguments.
    """
    items = kwds.items
    items.sort()
    key = (args, tuple(items)) # hope everything is hashable!
    return ( state.get(key)
             or state.setdefault(key, func(*args, **kwds)) )

def memoized2(func, state, *args, **kwds) [decorator2]:
    key = (args, tuple(kwds.items()))
    return ( state.get(key)
             or state.setdefault(key, func(*args, **kwds)) )

import sys, time
def logged(func, state, *args, **kwds) [decorator.fromstate(
                                            output=sys.stdout.write,
                                            prefix='%(func)s: ')]:
    """ Usage for default formatting and output:
   
            def func(*args) [logged]:
                pass

        Usage for custom formatting and output:
       
            def func(a, b) [logged.options(output=output_func,
                                           prefix="%(func)s %(a)s %(b)i")]:
                pass

        String replacements include 'func' which is the function's name, 
as well
        as all arguments to the function such that "%(a)s" would become 
the value
        of the argument named 'a'.
    """
    p = '[%s] %s' % (time.time(), state['prefix'] % state)
    state['output'](p)

def logged2(func, state, *args, **kwds) [decorator2(
                                            output=sys.stdout.write,
                                            prefix='%(func)s: ')]:
    """ Usage for default formatting and output:
   
            def func(*args) [logged2]:
                pass

        Usage for custom formatting and output:
       
            def func(a, b) [logged2(output=output_func,
                                   prefix="%(func)s %(a)s %(b)i")]:
                pass

        String replacements include 'func' which is the function's name, 
as well
        as all arguments to the function such that "%(a)s" would become 
the value
        of the argument named 'a'.
    """
    p = '[%s] %s' % (time.time(), state['prefix'] % state)
    state['output'](p)

# demo function
def factorial(n) [memoized,
                  logged.options(output=sys.stdout.write,
                                 prefix='factorial for %(n)s: ')]:
    """ Computes the factorial of n """
    print "fact(%i)" % n
    if n == 1:
        return 1
    else:
        return n * fact(n - 1) # make tail_recursive decorator(?)

def factorial2(n) [memoized,
                   logged2(output=sys.stdout.write,
                           prefix='factorial for %(s): ')]:
    """ Computes the factorial of n """
    print "fact(%i)" % n
    if n == 1:
        return 1
    else:
        return n * fact(n - 1)





More information about the Python-Dev mailing list