Decorator Base Class: Needs improvement.

S

Steve Holden

Ron_Adam said:
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--
I find I am still left asking the question "why would anyone want to do
that?".

The difference between a use case and an example is that a use case
should demonstrate the solution of a problem that someone might
reasonably be wanting to solve, rather than a way of creating an
abstract program structure for which there is no easily observable
requirement.

I can understand it if you are merely pursuing this topic because of
your fascination with the capabilities of Python, but I don't have the
feeling that there are legion Python programmers out there waiting
impatiently to be able to build wrapped functions. People have been
doing that as necessary for years, you're just coming to it for the
first time.

By the way, we pass *parameters* to functions, *perimeters* surround things.

regards
Steve
 
S

Scott David Daniels

Guess I am listening to language funny today.

Steve said:
By the way, we pass *parameters* to functions, *perimeters* surround
things.
But we do pass parameters *around*, which may be the source of the
confusion. :)

-Scott David Daniels
(e-mail address removed)
 
R

Ron_Adam

I find I am still left asking the question "why would anyone want to do
that?".


You didn't say which part you were referring too. As far as wrapping
the same exact wrapper more than once. You probably wouldn't do that.
It's just easier to test the use of multiple wrappers that way.

As far as reversing the stack of wrappers, that could matter. Because
the wrappers are nested, the inputs are done in forward order, and the
results are sent back out in reverse order. If you are applying
several different graphic filters to an image display function, the
order makes a difference, so depending on weather you are modifying
the input values, or the returned values, you may need to reverse the
wrappers before applying them.
The difference between a use case and an example is that a use case
should demonstrate the solution of a problem that someone might
reasonably be wanting to solve, rather than a way of creating an
abstract program structure for which there is no easily observable
requirement.

It was an example that demonstrates a basic capabilities of an
alternative approach, but it is also one solution to certain
sub-problems, simpler function wrapping, and adding and removing
wrappers at locations other than the function definitions. It's
probably not a new approach either.
I can understand it if you are merely pursuing this topic because of
your fascination with the capabilities of Python, but I don't have the
feeling that there are legion Python programmers out there waiting
impatiently to be able to build wrapped functions.

Yes, I am pursuing the topic because I enjoy experimenting, and
because I enjoy programming with Python, and I have an interest in
using it in solving real problems. So the answer is all of the above.
;-)

Cheers,
Ron
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,780
Messages
2,569,614
Members
45,291
Latest member
BlockchainInvestorDatabse

Latest Threads

Top