473,387 Members | 1,624 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,387 software developers and data experts.

Decorator Base Class: Needs improvement.


Hi, Thanks again for all the helping me understand the details of
decorators.

I put together a class to create decorators that could make them a lot
easier to use.

It still has a few glitches in it that needs to be addressed.

(1) The test for the 'function' object needs to not test for a string
but an object type instead.

(2) If the same decorator instance is stacked, it will get locked in a
loop. But stacking different instances created from the same
decorator object works fine.

(3) It has trouble if a decorator has more than one argument.
But I think all of these things can be fixed. Would this be something
that could go in the builtins library? (After any issues are fixed
first of course.)

When these are stacked, they process all the prepossess's first, call
the decorator, then process all the postprocess's. It just worked out
that way, which was a nice surprise and makes this work a bit
different than the standard decorators.

Cheers,
Ron

#---start---

class Decorator(object):
"""
Decorator - A class to make decorators with.

self.function - name of function decorated.
self.arglist - arguments of decorator
self.preprocess - over ride to preprocess function arguments.
self.postprocess - over ride to postprocess function
return value.

Example use:
class mydecorator(Decorate):
def self.preprocess(self, args):
# process args
return args
def self.postprocess(self, results):
# process results
return results

deco = mydecorator()

@deco
def function(args):
# function body
return args
"""
function = None
arglist = []
def __call__(self, arg):
self.arglist.append(arg)
def _wrapper( args):
pre_args = self.preprocess(args)
result = self.function(pre_args)
return self.postprocess(result)
if 'function' in str(arg):
self.arglist = self.arglist[:-1]
self.function = arg
return _wrapper
return self
def preprocess(self, args):
return args
def postprocess(self, result):
return result

class mydecorator(Decorater):
def preprocess(self, args):
args = 2*args
return args
def postprocess(self, args):
args = args.upper()
args = args + str(self.arglist[0])
return args
deco = mydecorator()

@deco('xyz')
def foo(text):
return text

print foo('abc')
#---end---
Jul 18 '05 #1
22 2196

Ok, that post may have a few(dozen?) problems in it. I got glitched
by idles not clearing variables between runs, so it worked for me
because it was getting values from a previous run.

This should work better, fixed a few things, too.

The decorators can now take more than one argument.
The function and arguments lists initialize correctly now.

It doesn't work with functions with more than one variable. It seems
tuples don't unpack when given to a function as an argument. Any way
to force it?
class Decorator(object):
"""
Decorator - A base class to make decorators with.

self.function - name of function decorated.
self.arglist - arguments of decorator
self.preprocess - over ride to preprocess function arguments.
self.postprocess - over ride to postprocess function
return value.

Example use:
class mydecorator(Decorate):
def self.preprocess(self, args):
# process args
return args
def self.postprocess(self, results):
# process results
return results

deco = mydecorator()

@deco
def function(args):
# function body
return args
"""
def __init__(self):
self.function = None
self.arglist = []
def __call__(self, *arg):
if len(arg) == 1:
arg = arg[0]
self.arglist.append(arg)
def _wrapper( *args):
if len(args) == 1:
args = args[0]
pre_args = self.preprocess(args)
result = self.function(pre_args)
return self.postprocess(result)
if 'function' in str(arg):
self.arglist = self.arglist[:-1]
self.function = arg
return _wrapper
return self
def preprocess(self, args):
return args
def postprocess(self, result):
return result
#---3---
class mydecorator(Decorator):
def preprocess(self, args):
args = 2*args
return args
def postprocess(self, args):
args = args.upper()
args = args + str(self.arglist[0])
return args
deco = mydecorator()

@deco('xyz')
def foo(text):
return text
print foo('abc')
#---2---
class decorator2(Decorator):
def preprocess(self, args):
return args+sum(self.arglist[0])
def postprocess(self, args):
return args
deco2 = decorator2()

@deco2(1,2)
def foo(a):
return a
print foo(1)

# This one doesn't work yet.
#---3---
class decorator3(Decorator):
pass
deco3 = decorator3()

@deco3
def foo(a,b):
return a,b
print foo(1,3)
Jul 18 '05 #2
Ron_Adam wrote:
Ok, that post may have a few(dozen?) problems in it. I got glitched
by idles not clearing variables between runs, so it worked for me
because it was getting values from a previous run.

This should work better, fixed a few things, too.

The decorators can now take more than one argument.
The function and arguments lists initialize correctly now.
Ron:

I've followed your attempts to understand decorators with interest, and
have seen you engage in conversation with many luminaries of the Python
community, so I hesitate at this point to interject my own remarks.

In a spirit of helpfulness, however, I have to ask whether your
understanding of decorators is different from mine because you don't
understand them or because I don't.

You have several times mentioned the possibility of a decorator taking
more than one argument, but in my understanding of decorators this just
wouldn't make sense. A decorator should (shouldn't it) take precisely
one argument (a function or a method) and return precisely one value (a
decorated function or method).
It doesn't work with functions with more than one variable. It seems
tuples don't unpack when given to a function as an argument. Any way
to force it?
class Decorator(object):

[...]

Perhaps we need to get back to basics?

Do you understand what I mean when I say a decorator should take one
function as its argument and it should return a function?

regards
Steve
--
Steve Holden +1 703 861 4237 +1 800 494 3119
Holden Web LLC http://www.holdenweb.com/
Python Web Programming http://pydish.holdenweb.com/

Jul 18 '05 #3
Steve Holden wrote:
You have several times mentioned the possibility of a decorator taking more than one argument, but in my understanding of decorators this just wouldn't make sense. A decorator should (shouldn't it) take precisely one argument (a function or a method) and return precisely one value (a decorated function or method).


Yes. I think this sould be fixed into the minds of the people exacly
this way You state it:

When writing

@decorator(x,y)
def f():
....

not the so called "decorator" function but decorator(x,y) is the
decorating function and decorator(x,y) is nothing but a callable object
that takes f as parameter. A little correcture of Your statement: it is
NOT nessacary that a function or method will be returned from a
decorator.

def decorator(x,y):
def inner(func):
return x+y
return inner

@decorator(1,2)
def f():pass
f

3

This is perfectly valid allthough not very usefull ;)

Regards,
Kay

Jul 18 '05 #4
On 5 Apr 2005 00:54:25 -0700, "Kay Schluehr" <ka**********@gmx.net> wrote:
Steve Holden wrote:
You have several times mentioned the possibility of a decoratortaking
more than one argument, but in my understanding of decorators this

just
wouldn't make sense. A decorator should (shouldn't it) take precisely

one argument (a function or a method) and return precisely one value

(a
decorated function or method).

I agree from an English language point of view. I.e., a verber is
something that does the verbing, so a decorator ought to be the thing
that does the decorating, which is the function/callable(s) resulting
on stack from the evaluation of the @-line.

In the case of a single @deco name, the evaluation is trivial, and
the difference between the @-expression and the resulting callable
might be overlooked. Full-fledged general expressions after the '@'
are for some reason disallowed, but it is handy to allow attribute
access and calling in the syntax, so the relevant Grammar rules are:

From the 2.4 Grammar, the key part seems to be

decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
decorators: decorator+
funcdef: [decorators] 'def' NAME parameters ':' suite

and further on

dotted_name: NAME ('.' NAME)*

So the Python Grammar's name for the @-expression is just plain "decorator"
which conflicts with my English-based reading of the word ;-/

So it appears the intent is to call the entire @-line the "decorator"
and I guess to have a name for what evaluating the @-line returns on the
stack, we could call it the "decorating callable" since it is what takes
the function parameter as its first parameter and decorates the function
and returns the decorated function.

But I don't like it, English-wise. I would rather call the @-line
the "decorator expression" and what it evaluates to the "decorator."

Can we change the grammar with s/decorator/decorator_expr/ ?
Yes. I think this sould be fixed into the minds of the people exacly
this way You state it: I think we may be agreeing in principle but with different words ;-)
When writing

@decorator(x,y)
def f():
....

not the so called "decorator" function but decorator(x,y) is the ^--(the result of evaluating)decorating function and decorator(x,y) is nothing but a callable object ^--(the result of evaluating)that takes f as parameter. A little correcture of Your statement: it is
NOT nessacary that a function or method will be returned from a
decorator.
Yes, in fact it could even perversely be colluding with a known succeeding
decorator callable to pass info strangely, doing strange things, e.g.,
trick = ['spam', 'eggs']
def choose_name(tup): ... nx, f = tup
... f.func_name = trick[nx]
... return f
... def namedeco(nx=1): ... return lambda f, nx=nx:(nx, f)
... @choose_name ... @namedeco()
... def foo(): pass
... foo <function eggs at 0x02EE8E64> @choose_name ... @namedeco(0)
... def foo(): pass
... foo <function spam at 0x02EE8DF4>

I.e., namedeco evaluates to the lambda as decorator function,
and that passes a perverse (nx, f) tuple on to choose_name, instead
of a normal f.

def decorator(x,y):
def inner(func):
return x+y
return inner

@decorator(1,2)
def f():pass
f

3

This is perfectly valid allthough not very usefull ;)


Perhaps even less useful, the final decorator can return something
arbitrary, as only the name in the def matters at that point in
the execution (as a binding target name), so:
def dumbdeco(f): return 'something dumb' ... @dumbdeco ... def foo(): pass
... foo 'something dumb'

Hm, maybe some use ...
def keydeco(name): ... return lambda f: (name, f)
... @keydeco('pooh_foo') ... def foo(): pass
... @keydeco('tigger_bar') ... def bar(): pass
... dict([foo, bar])

{'pooh_foo': <function foo at 0x02EE8DBC>, 'tigger_bar': <function bar at 0x02EE8DF4>}

.... nah ;-)
Anyway, I think a different name for what comes after the "@" and the
callable that that (very limited) expression is supposed to return
would clarify things. My conceptual model is

@decorator_expression # => decorator
def decorating_target(...):
...

Regards,
Bengt Richter
Jul 18 '05 #5
Bengt Richter wrote:
From the 2.4 Grammar, the key part seems to be

decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
decorators: decorator+
funcdef: [decorators] 'def' NAME parameters ':' suite

and further on

dotted_name: NAME ('.' NAME)*

So the Python Grammar's name for the @-expression is just plain "decorator" which conflicts with my English-based reading of the word ;-/


What about playing with the words decorator/decoration? The allegoric
meaning of the decorator

def deco(f):
pass

would be: f is "vanishing under decoration" - all is vanity.

In the example deco would be both a decorator and a decoration. In
other examples deco were a decorator and deco(x,y) the decoration.

Regards,
Kay

Jul 18 '05 #6
Bengt Richter wrote:
From the 2.4 Grammar, the key part seems to be

decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
decorators: decorator+
funcdef: [decorators] 'def' NAME parameters ':' suite

and further on

dotted_name: NAME ('.' NAME)*

So the Python Grammar's name for the @-expression is just plain "decorator" which conflicts with my English-based reading of the word ;-/


What about playing with the words decorator/decoration? The allegoric
meaning of the decorator

def deco(f):
pass

would be: f is "vanishing under decoration" - all is vanity.

In the example deco would be both a decorator and a decoration. In
other cases deco were a decorator and deco(x,y) the decoration.

Regards,
Kay

Jul 18 '05 #7
On Tue, 05 Apr 2005 02:55:35 -0400, Steve Holden <st***@holdenweb.com>
wrote:
Ron_Adam wrote:
Ok, that post may have a few(dozen?) problems in it. I got glitched
by idles not clearing variables between runs, so it worked for me
because it was getting values from a previous run.

This should work better, fixed a few things, too.

The decorators can now take more than one argument.
The function and arguments lists initialize correctly now.
Ron:

I've followed your attempts to understand decorators with interest, and
have seen you engage in conversation with many luminaries of the Python
community, so I hesitate at this point to interject my own remarks.


I don't mind. It might help me communicate my ideas better.
In a spirit of helpfulness, however, I have to ask whether your
understanding of decorators is different from mine because you don't
understand them or because I don't.
Or it's just a communication problem, and we both understand.
Communicating is not my strongest point. But I am always willing to
clarify something I say.
You have several times mentioned the possibility of a decorator taking
more than one argument, but in my understanding of decorators this just
wouldn't make sense. A decorator should (shouldn't it) take precisely
one argument (a function or a method) and return precisely one value (a
decorated function or method).
It doesn't work with functions with more than one variable. It seems
tuples don't unpack when given to a function as an argument. Any way
to force it?
What I was referring to is the case:

@decorator(x,y,z)

As being a decorator expression with more than one argument. and not:

@decorator(x)(y)

This would give a syntax error if you tried it.
@d1(1)(2)
SyntaxError: invalid syntax

The problem I had with tuple unpacking had nothing to do with
decorators. I was referring to a function within the class, and I
needed to be consistent with my use of tuples as arguments to
functions and the use of the '*' indicator.
Do you understand what I mean when I say a decorator should take one
function as its argument and it should return a function?

regards
Steve


Hope this clarifies things a bit.

Cheers,
Ron
Jul 18 '05 #8
Ron_Adam wrote:
What I was referring to is the case:
@decorator(x,y,z)
As being a decorator expression with more than one argument. But, we generally say this is a call to a function named decorator
that returns a decorator. If you called it:
@make_decorator(x,y)
def .....
We'd be sure we were all on the same page.

How about this as an example:

def tweakdoc(name):
def decorator(function):
function.__doc__ = 'Tweak(%s) %r' % (name, function.__doc__)
return function
return decorator

What is confusing us about what you write is that you are referring to
tweakdoc as a decorator, when it is a function returning a decorator.
and not:
@decorator(x)(y)


This is only prevented by syntax (probably a good idea, otherwise
we would see some very complicated expressions before function
declarations).

--Scott David Daniels
Sc***********@Acm.Org
Jul 18 '05 #9
On Tue, 05 Apr 2005 14:32:59 -0700, Scott David Daniels
<Sc***********@Acm.Org> wrote:
Ron_Adam wrote:
What I was referring to is the case:
@decorator(x,y,z)
As being a decorator expression with more than one argument.But, we generally say this is a call to a function named decorator
that returns a decorator. If you called it:
@make_decorator(x,y)
def .....
We'd be sure we were all on the same page.


Good point, I agree. :)

Or alternatively
@call_decorator(x,y)

Using either one would be good practice.
How about this as an example:

def tweakdoc(name):
def decorator(function):
function.__doc__ = 'Tweak(%s) %r' % (name, function.__doc__)
return function
return decorator

What is confusing us about what you write is that you are referring to
tweakdoc as a decorator, when it is a function returning a decorator.


Bengt Richter is also pointing out there is an inconsistency in
Pythons documents in the use of decorator. I've been trying to start
referring to the "@___" as the decorator-exression, but that still
doesn't quite describe what it does either. Decorarator-caller might
be better. Then the decorator-function as the part that defines the
decorated-function.

Another alternative is to call the entire process what it is,
function-wrapping. Then the "@____" statement would be the
wrapper-caller, which calls the wrapper-function, which defines the
wrapped-function. That's much more descriptive to me.

If we do that then we could agree to use decorator as a general term
to describe a function as decorated. Meaning it is wrapped and get
away from the decorator/decoratoree discussions. But I think that the
terminology has been hashed out quite a bit before, so I don't expect
it to change. I'll just have to try to be clearer in how I discuss
it.
and not:
@decorator(x)(y)


This is only prevented by syntax (probably a good idea, otherwise
we would see some very complicated expressions before function
declarations).

--Scott David Daniels
Sc***********@Acm.Org


And this isn't allowed either, although it represents more closely the
nesting that takes place when decorator-expressions are stacked.

@make_deco1
@make_deco2
@make_deco3
def function1(n):
n+=1
return n
This is allowed, but its not pretty.

@make_deco1
@ make_deco2
@ make_deco3
def function1(n):
n+=1
return n
Cheers,
Ron
Jul 18 '05 #10
Ron_Adam wrote:
On Tue, 05 Apr 2005 02:55:35 -0400, Steve Holden <st***@holdenweb.com>
wrote:

Ron_Adam wrote:
Ok, that post may have a few(dozen?) problems in it. I got glitched
by idles not clearing variables between runs, so it worked for me
because it was getting values from a previous run.

This should work better, fixed a few things, too.

The decorators can now take more than one argument.
The function and arguments lists initialize correctly now.


Ron:

I've followed your attempts to understand decorators with interest, and
have seen you engage in conversation with many luminaries of the Python
community, so I hesitate at this point to interject my own remarks.

I don't mind. It might help me communicate my ideas better.

In a spirit of helpfulness, however, I have to ask whether your
understanding of decorators is different from mine because you don't
understand them or because I don't.

Or it's just a communication problem, and we both understand.
Communicating is not my strongest point. But I am always willing to
clarify something I say.

You have several times mentioned the possibility of a decorator taking
more than one argument, but in my understanding of decorators this just
wouldn't make sense. A decorator should (shouldn't it) take precisely
one argument (a function or a method) and return precisely one value (a
decorated function or method).

It doesn't work with functions with more than one variable. It seems
tuples don't unpack when given to a function as an argument. Any way
to force it?

What I was referring to is the case:

@decorator(x,y,z)

As being a decorator expression with more than one argument. and not:

@decorator(x)(y)

This would give a syntax error if you tried it.

@d1(1)(2)


SyntaxError: invalid syntax

The problem I had with tuple unpacking had nothing to do with
decorators. I was referring to a function within the class, and I
needed to be consistent with my use of tuples as arguments to
functions and the use of the '*' indicator.

Do you understand what I mean when I say a decorator should take one
function as its argument and it should return a function?

regards
Steve

Hope this clarifies things a bit.

Cheers,
Ron

So what you are saying is that you would like to be able to use
arbitrarily complex expressions after the :at" sign, as long as they
return a decorator? If so, you've been "pronounced" :-)

regards
Steve
--
Steve Holden +1 703 861 4237 +1 800 494 3119
Holden Web LLC http://www.holdenweb.com/
Python Web Programming http://pydish.holdenweb.com/

Jul 18 '05 #11
Scott David Daniels wrote:
Ron_Adam wrote:
...
def tweakdoc(name):
def decorator(function):
function.__doc__ = 'Tweak(%s) %r' % (name, function.__doc__)
return function
return decorator

What is confusing us about what you write is that you are referring

to tweakdoc as a decorator, when it is a function returning a decorator.


"Decorator factory" would be a shorter name for "a function returning a
decorator".

Jul 18 '05 #12
On Tue, 05 Apr 2005 19:38:38 -0400, Steve Holden <st***@holdenweb.com>
wrote:


So what you are saying is that you would like to be able to use
arbitrarily complex expressions after the :at" sign, as long as they
return a decorator? If so, you've been "pronounced" :-)

regards
Steve


No not at all, I never said that. But.. ;-)
If we get into what I would like, as in my personal wish list, that's
a whole other topic. <g>
I would have preferred the @ symbol to be used as an inline assert
introducer. Which would have allowed us to put debug code anywhere we
need. Such as @print total @. Then I can switch on and off
debugging statements by setting __debug__ to True or False where ever
I need it.

And as far as decorators go. I would of preferred a keyword, possibly
wrap, with a colon after it. Something like this.

def function_name(x):
return x

wrap function_name:
wrapper1()
wrapper2()
wrapper3()

A wrap command could more directly accomplish the wrapping, so that
def statements within def statements aren't needed. (Unless you
want'ed too for some reason.)

And as far as arbitrary complex expressions go.. Actually I think that
it's quite possible to do as it is. ;-)

But this is just a few of my current thoughts which may very well
change. It's an ever changing list. <g>

Cheers,
Ron

Jul 18 '05 #13
Ron_Adam wrote:
I would have preferred the @ symbol to be used as an inline assert
introducer. Which would have allowed us to put debug code anywhere we
need. Such as @print total @.


Don't lose heart, there are still two unused characters
left, $ and ?.

? might even be more mnemonic for this purpose, as

?print "foo =", foo

has a nice hint of "WT?%$%$ is going on at this point?"
to it.

--
Greg Ewing, Computer Science Dept,
University of Canterbury,
Christchurch, New Zealand
http://www.cosc.canterbury.ac.nz/~greg
Jul 18 '05 #14
On Wed, 06 Apr 2005 16:33:24 +1200, Greg Ewing
<gr**@cosc.canterbury.ac.nz> wrote:
Ron_Adam wrote:
I would have preferred the @ symbol to be used as an inline assert
introducer. Which would have allowed us to put debug code anywhere we
need. Such as @print total @.


Don't lose heart, there are still two unused characters
left, $ and ?.

? might even be more mnemonic for this purpose, as

?print "foo =", foo

has a nice hint of "WT?%$%$ is going on at this point?"
to it.


LOL, yes, it does. :-)
Jul 18 '05 #15
On 5 Apr 2005 19:28:55 -0700, "El Pitonero" <pi******@gmail.com> wrote:
Scott David Daniels wrote:
Ron_Adam wrote:
> ...


def tweakdoc(name):
def decorator(function):
function.__doc__ = 'Tweak(%s) %r' % (name, function.__doc__)
return function
return decorator

What is confusing us about what you write is that you are referring

to
tweakdoc as a decorator, when it is a function returning a decorator.


"Decorator factory" would be a shorter name for "a function returning a
decorator".

True, but tweakdoc doesn't have to be a function, so IMO we need a better
name for the @-line, unless you want to use many various specific names
like factory. E.g.,

(Preliminary definition):
class Deco(object): ... class __metaclass__(type):
... def __getattribute__(cls, name):
... if hasattr(type(cls), name): return type.__getattribute__(cls, name)
... def decorator(f):
... f.func_name = '%s_%s'%(f.func_name, name)
... return f
... return decorator
... deco = Deco
Ok, now what do you call this @-line? Freaky decorator factory invocation?
Decoratoriferous expression? ;-) Lexical decoration?
Decorator{-producing,-generating, ??} expression ?

Decorator expression for short? vs the decorator callable produced.
@deco.one ... def foo(): pass
... foo <function foo_one at 0x02EE8E2C> @deco.three ... @deco.two
... def bar():pass
... bar

<function bar_two_three at 0x02EE8E9C>

Regards,
Bengt Richter
Jul 18 '05 #16
On Wed, 06 Apr 2005 02:31:05 GMT, Ron_Adam <ra****@tampabay.rr.com> wrote:
On Tue, 05 Apr 2005 19:38:38 -0400, Steve Holden <st***@holdenweb.com>
wrote:

So what you are saying is that you would like to be able to use
arbitrarily complex expressions after the :at" sign, as long as they
return a decorator? If so, you've been "pronounced" :-)

regards
Steve


No not at all, I never said that. But.. ;-)
If we get into what I would like, as in my personal wish list, that's
a whole other topic. <g>
I would have preferred the @ symbol to be used as an inline assert
introducer. Which would have allowed us to put debug code anywhere we
need. Such as @print total @. Then I can switch on and off
debugging statements by setting __debug__ to True or False where ever
I need it.

And as far as decorators go. I would of preferred a keyword, possibly
wrap, with a colon after it. Something like this.


I don't understand your seeming fixation with wrappers and wrapping. That's not
the only use for decorators. See Raymond Hettinger's optimizing decorators
in the cookbook for instance. Decorators are something like metaclasses for functions,
with much more general possibilities than wrapping, IMO.

def function_name(x):
return x

wrap function_name:
wrapper1()
wrapper2()
wrapper3()

A wrap command could more directly accomplish the wrapping, so that
def statements within def statements aren't needed. (Unless you
want'ed too for some reason.) I think you'll have to show some convincing use cases showing a clear
advantage over current decoration coding if you want converts ;-)

And as far as arbitrary complex expressions go.. Actually I think that
it's quite possible to do as it is. ;-) Yes and no. You can always write @deco and refer to an arbitrarily complex
callable deco ..., but the actual @-line expression has to abide by the language grammar,
and that does not allow arbitrary expressions. But it does provide enough rope.
E.g. see my most recent reply to El Pitonero.

But this is just a few of my current thoughts which may very well
change. It's an ever changing list. <g>

I can see that ;-)

Regards,
Bengt Richter
Jul 18 '05 #17
Bengt Richter wrote:
On 5 Apr 2005 19:28:55 -0700, "El Pitonero" <pi******@gmail.com> wrote:
Scott David Daniels wrote:
Ron_Adam wrote:
> ...

def tweakdoc(name):
def decorator(function):
function.__doc__ = 'Tweak(%s) %r' % (name, function.__doc__)
return function
return decorator

What is confusing us about what you write is that you are
referringto
tweakdoc as a decorator, when it is a function returning a
decorator.
"Decorator factory" would be a shorter name for "a function returning adecorator".
True, but tweakdoc doesn't have to be a function, so IMO we need a

better name for the @-line, unless you want to use many various specific names like factory. E.g.,


There are two things:

(1) The "tweadoc" object in the example, which no doubt can be called a
decorator factory.

(2) The @-line, which you called a "decorator expression" and that's
fine with me. My preference would be something like the "decorator
header". A more clear statement would be something like: a "decorator
header expression" or the "expression in the decorator header", though
your proposed "decorator expression" would be clear enough, too.

I was addressing (1). You jumped in with (2), which I was aware of and
was not dissenting.

Jul 18 '05 #18
Bengt Richter wrote:
@deco.one
def foo(): pass Ok, now what do you call this @-line? Freaky decorator factory invocation?
Decoratoriferous expression? ;-) This one has a certain je ne sais quoi ;-), but I'd call "deco.one"
the decorator function (since it _must_ evaluate to a function), and
if it needs more clarity, of your non-oderiferous proposals, I'd take: Decorator expression for short?


--Scott David Daniels
Sc***********@Acm.Org
Jul 18 '05 #19
On Wed, 06 Apr 2005 08:10:22 GMT, bo**@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--

Jul 18 '05 #20
Ron_Adam wrote:
On Wed, 06 Apr 2005 08:10:22 GMT, bo**@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--

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
--
Steve Holden +1 703 861 4237 +1 800 494 3119
Holden Web LLC http://www.holdenweb.com/
Python Web Programming http://pydish.holdenweb.com/

Jul 18 '05 #21
Guess I am listening to language funny today.

Steve Holden wrote:
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
Sc***********@Acm.Org
Jul 18 '05 #22
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

Jul 18 '05 #23

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

2
by: lcaamano | last post by:
We have a tracing decorator that automatically logs enter/exits to/from functions and methods and it also figures out by itself the function call arguments values and the class or module the...
5
by: Doug | last post by:
I am looking at using the decorator pattern to create a rudimentary stored proc generator but am unsure about something. For each class that identifies a part of the stored proc, what if I want to...
1
by: Doug | last post by:
I am looking at using the decorator pattern to create a rudimentary stored proc generator but am unsure about something. For each class that identifies a part of the stored proc, what if I want to...
9
by: Raymond Hettinger | last post by:
I had an idea but no time to think it through. Perhaps the under-under name mangling trick can be replaced (in Py3.0) with a suitably designed decorator. Your challenge is to write the decorator....
9
by: Christian Hackl | last post by:
Hi! I've got a design question related to the combination of the NVI idiom (non-virtual interfaces, ) and popular object-oriented patterns such as Proxy or Decorator, i.e. those which have the...
4
by: thomas.karolski | last post by:
Hi, I would like to create a Decorator metaclass, which automatically turns a class which inherits from the "Decorator" type into a decorator. A decorator in this case, is simply a class which...
11
by: George Sakkis | last post by:
I have a situation where one class can be customized with several orthogonal options. Currently this is implemented with (multiple) inheritance but this leads to combinatorial explosion of...
8
by: Chris Forone | last post by:
hello group, is there a possibility to implement the decorator-pattern without new/delete (nor smartpt)? if not, how to ensure correct deletion of the objects? thanks & hand, chris
8
by: Bryan | last post by:
I want my business objects to be able to do this: class Person(base): def __init__(self): self.name = None @base.validator def validate_name(self): if not self.name: return
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.