Decorator Base Class: Needs improvement.

Ron_Adam radam2 at tampabay.rr.com
Wed Apr 6 14:04:31 EDT 2005


On Wed, 06 Apr 2005 08:10:22 GMT, bokr at oz.net (Bengt Richter) wrote:

>I don't understand your seeming fixation with wrappers and wrapping.

Fixated implies, I'm stuck on a single thing, but I'm not. I am
learning as I go, and exploring some possibilities as well.  :-)

> That's not
>the only use for decorators. See Raymond Hettinger's optimizing decorators
>in the cookbook for instance. 

Thanks, I'll look for it.  Is it in the new edition?  I haven't picked
it up yet.

>Decorators are something like metaclasses for functions,
>with much more general possibilities than wrapping, IMO.

I'm not sure I see the metaclass relation.  What more general things
can be done with Decorators, that can't be done with a wrapper?

Wrapping, and the @decorator expressions, interest me because I see a
lot of potential in it's use, and so I'm trying to learn them, and at
the same time, there are things about the @ expression that seems (to
me), that it's not the most practical way to do what it was intended
for.  

On the plus side, it's kind of cute with the little curly thing
propped up on top of the function. It's a neat trick that it does what
it does with a minimal amount of changes to the language by taking
advantage of pythons existing function perimeter and object passing
properties. It saves a bit of typing because we don't have to retype
the function name a few times.  (Several people have referred to it as
'sugar', and now I am starting to agree with that opinion.)

On the minus side, it's not intuitive to use.  It is attached to the
function definitions so they are limited, they can't be easily
unwrapped and rewrapped without redefining the function also.  The
recursive nature of stacked @ statements is not visible.

So my opinion of @ as a whole is currently: -1


>I think you'll have to show some convincing use cases showing a clear
>advantage over current decoration coding if you want converts ;-)

What about the following?  :-)

# Using this simple wrapper class:
class wrapper(object):
    def __call__(self,x):
       # preprocess x
       x*=2 			# Make a change so we can see it
       result = self.function(x)
       # postprocuess result
       return result

# A function to apply the wrapper:
def wrap(function,wrapper):
    w = wrapper()
    w.function = function
    return w

# The function
def fn(x):
    return x

print fn(5)		# Before

# Wrapit.
fn = wrap(fn,wrapper)

print fn(5)		# After

# Unwrap it.
fn = fn.function

print fn(5)     # And back again

#prints
#5
#10
#5

It has several advantages over @ expression.  It doesn't need the
triple nested defines to get the function name and argument list, the
wrapper is simpler, It can be placed on a function and then removed,
when and where it's needed, instead of at the point where the function
is defined.

The following behaves more closely to the existing @ expression in
that it has the same nesting behavior for stacked wrappers.  

I'm looking into a way to do sequential non-nested stacked wrappers at
this point, where the output of one goes to the input of the next.
That can't be done currently with the @ decorator expression.

This stacks a list of 10 wrappers on 10 different functions and
reverses the order of the stack every other function. In this case
they are all the same, but they could all be differnt. 

Cheers,
Ron

#---start---
class wrapper(object):
    def __call__(self,*x):
        # preprocess
        x = [x[0]+1,] 
        print 'preprocess', x[0], self.args
        # call function
        result = self.function(*x)
        # postprocess
        result +=1
        print 'postprocess', result, self.args
        return result

def wrap(f,w,shape='forward'):
    if shape=='reverse':
        w.reverse()
    for ww in w:
        nw = wrapper()
        try:
            nw.args = ww[1]
        except TypeError:
            wf = ww[0]
        nw.function = f
        f = nw
    return f

# Make a list of ten wrappers with an id number as an additional
# wrapper perimeter.
w = []
for n in xrange(10):
    w.append((wrapper,n))
    
# Wrap 10 functions, 10 times, in reversing order.
def func0(x): return x
def func1(x): return x
def func2(x): return x
def func3(x): return x
def func4(x): return x
def func5(x): return x
def func6(x): return x
def func7(x): return x
def func8(x): return x
def func9(x): return x

func0 = wrap(func0,w)
func1 = wrap(func1,w,'reverse')
func2 = wrap(func2,w)
func3 = wrap(func3,w,'reverse')
func4 = wrap(func4,w)
func5 = wrap(func5,w,'reverse')
func6 = wrap(func6,w)
func7 = wrap(func7,w,'reverse')
func8 = wrap(func8,w)
func9 = wrap(func9,w,'reverse')

print func0(0)
print func1(0)
print func2(0)
print func3(0)
print func4(0)
print func5(0)
print func6(0)
print func7(0)
print func8(0)
print func9(0)

#--end--






More information about the Python-list mailing list