Decorators

  • Thread starter Colin J. Williams
  • Start date
C

Colin J. Williams

Christopher T. King suggested that "we're trying to kill too many birds
with one stone".

http://groups.google.com/[email protected]&rnum=1

He goes on to suggest three needs which decorators serve. Are these the
only purposes which are envisaged for decorators?

Most of the discussion has focused on syntax. I would like to see some
clarification of the purpose and meaning of a decoration.

The latest version of PEP 318 has a Motivation section which talks of
transforming a method or function. The possible purposes of the
transformation is not so clear.

The use of the term decorator is questionable. In everyday usage, to
decorate is to furnish with adornments. In this sense, we could say
that a docstring is a decorator, but is that true of a transformation.
It seems desirable that we choose words which are as close as possible
to their everyday usage.

Example 4 in the PEP 318 illustrates the enforcement of function/method
argument and return checking. Is it envisaged that the accepts and
returns functions become part of __builtins__? The code seems to assume
that the interpreter will divert the func arguments to accepts and the
return object to returns. Is this correct? If so, how many trigger
words are implemented?

Some syntactic options are provided in:
http://www.python.org/moin/PythonDecorators
I am inclined to support option D. The decorator follows the function
definition and is indented, indicating that the decorator qualifies the
decorated and is, in a sense subordinate to it. It doesn't use @.

Steve Bethard sets out the syntactic choices clearly:

http://groups.google.com/[email protected]&rnum=1

My choices would be:

Indicator: Keyword preferred, but not | (vertical bar), as it looks too
much like 1 or l.

Location: First line of the body. The decorators are subordinate to the
signature and indented like the docstring.

List notation: As ListType, with a line per entry.

Indented: Yes, no block.

Clearly, I don't fully understand PEP 318, but I wonder at times is
decorators aren't a solution in search of a problem.

Colin W.
 
R

Roy Smith

Colin J. Williams said:
It seems desirable that we choose words which are as close as possible
to their everyday usage.

On the other hand, it's desirable to pick words which have established
meanings in computer science too. The concept of a "decorator" is
pretty well established. The term has been used in GUI frameworks for
10 or 15 years, and more recently has been enshrined in various pattern
collections.
 
J

Jarek Zgoda

Roy Smith said:
On the other hand, it's desirable to pick words which have established
meanings in computer science too. The concept of a "decorator" is
pretty well established. The term has been used in GUI frameworks for
10 or 15 years, and more recently has been enshrined in various pattern
collections.

In GUI frameworks it was used for other things IIRC. Also "decorator
pattern" in GOF means something different.
 
D

Dan Bishop

Colin J. Williams said:
Christopher T. King suggested that "we're trying to kill too many birds
with one stone".

http://groups.google.com/[email protected]&rnum=1

He goes on to suggest three needs which decorators serve. Are these the
only purposes which are envisaged for decorators?

If I understand correctly, they'd be useful for anything where you'd
now use the syntax

function = decorator(function)

In addition to @staticmethod, you could have decorators for

(1) Memoization. Makes repeated function evaluation more efficient
without having to rewrite the function.

class memoize(object):
def __init__(self, func):
self.__func = func
self.__results = {}
def __call__(self, *args):
if args not in self.__results:
self.__results[args] = self.__func(*args)
return self.__results[args]

def fibonacci(n):
@memoize
if n in (0, 1):
return n
return fibonacci(n - 1) + fibonacci(n - 2)

(2) Debugging uses, like:

class printreturns(object):
"Behaves like f but prints its return values."
def __init__(self, f):
self.__f = f
def __call__(self, *args):
result = self.__f(*args)
if debug:
print 'f%r = %r' % (args, result)
return

def somefunc(x, y):
@printreturns
...
 
C

Colin J. Williams

Roy said:
On the other hand, it's desirable to pick words which have established
meanings in computer science too. The concept of a "decorator" is
pretty well established. The term has been used in GUI frameworks for
10 or 15 years, and more recently has been enshrined in various pattern
collections.
I guess that it depends partly on whether the majority of Python users
consider themselves to be computer scientists.

Colin W.
 
A

Arthur

On the other hand, it's desirable to pick words which have established
meanings in computer science too. The concept of a "decorator" is
pretty well established. The term has been used in GUI frameworks for
10 or 15 years, and more recently has been enshrined in various pattern
collections.

My one line answer to the question of what it is I like about Python
is:

"""
It is literate without being effete.
"""

I can live with "decorator", because it is a word without a particular
meaning to me (in this general realm), and I can fill in that blank by
an understanding of what it means in concrete terms in Python. But it
disturbs me (mildly) to see it justified by something like the Gang of
Four reference or GUI framework stuff.

I have no compelling reason to read the GOF material. But feel myself
to be rarefied (by walking around kind of folks statndards) by
understanding, broadly, the reference. I can think of numbers of folks
I know who make a living in IT related work, and for whom I am
confident the reference would draw a blank.
..
I've written (simple) GUIs and never heard the word decorator used in
that context.

This is getting to be pretty effete stuff for a language that strives
to be for everybody. Or is that silliness finally off the table.

..
Standalone the issue of the justification of the word
"decorator"wouldn't motivate me to comment.


But then I just read something defending '@' because it evokes
"prepocessor". Which also then justifies, I guess, its position
relative to what it processes.

But it also - is it not - the thingy that proceeds "aol.com" when
writing to manny_man.

I am a great believer in (motivated ) folks capacity to learn new
things. And the existence of synonyms is not news to anyone. So no,
nobody motivated to learn new things will be stopped in their tracks
by @ in a new context (anymore than anyone motivated to learn new
things things would - given an explanation - be stopped in their
tracks by 1/2 = 0 ).

But there is something a bit effete about a confident statement that
'@' is consistent with prepended information by referencing its use
(somewhere apparently) to hold prepocessor directives..

The less effete argument, it seems to me, is the opposite, The method
is the addressee, the decorator the address. The @ therefore belongs
between them - at the top of the body of the method. Not before it.

Art
 
D

Dan Bishop

Arthur said:
I am a great believer in (motivated ) folks capacity to learn new
things. And the existence of synonyms is not news to anyone. So no,
nobody motivated to learn new things will be stopped in their tracks
by @ in a new context

I agree.
(anymore than anyone motivated to learn new
things things would - given an explanation - be stopped in their
tracks by 1/2 = 0 ).

I never had a problem with 1/2 == 0; I was already used to it from C, C++, and Java.

But I did get stopped in my tracks the time a function that contained a line like

mean = sum(seq) / len(seq)

failed because the list happened not to contain any floats.
 
A

Arthur

I agree.


I never had a problem with 1/2 == 0; I was already used to it from C, C++, and Java.

But I did get stopped in my tracks the time a function that contained a line like

mean = sum(seq) / len(seq)

failed because the list happened not to contain any floats.

Not to get off on it.

But you should be aware that I am 100% in agreement with the good
arguments related to the this issue, and 100% against the bad ones.



;)


Art
 
C

Colin J. Williams

Dan said:
Colin J. Williams said:
Christopher T. King suggested that "we're trying to kill too many birds
with one stone".

http://groups.google.com/[email protected]&rnum=1

He goes on to suggest three needs which decorators serve. Are these the
only purposes which are envisaged for decorators?


If I understand correctly, they'd be useful for anything where you'd
now use the syntax

function = decorator(function)

In addition to @staticmethod, you could have decorators for

(1) Memoization. Makes repeated function evaluation more efficient
without having to rewrite the function.

class memoize(object):
def __init__(self, func):
self.__func = func
self.__results = {}
def __call__(self, *args):
if args not in self.__results:
self.__results[args] = self.__func(*args)
return self.__results[args]

def fibonacci(n):
@memoize
if n in (0, 1):
return n
return fibonacci(n - 1) + fibonacci(n - 2)

(2) Debugging uses, like:

class printreturns(object):
"Behaves like f but prints its return values."
def __init__(self, f):
self.__f = f
def __call__(self, *args):
result = self.__f(*args)
if debug:
print 'f%r = %r' % (args, result)
return

def somefunc(x, y):
@printreturns
...

Dan,
Many thanks for this, the examples helped to clarify things.

I've attached a script which shows a significant time benefit for
memoize. Also, it shows about a 24% reduction in the elapsed time
as compared with version 2.3.

I had expected that the unwrapped version of fibonacci would be
better than the recursive method as it avoided the overhead of
function calls. This test shows the reverse.

Colin W.

# tFib.py
#example by Dan Bishop of decorator usage

class memoize(object):
def __init__(self, func):
self.__func = func
self.__results = {}
def __call__(self, *args):
if args not in self.__results:
self.__results[args] = self.__func(*args)
return self.__results[args]

@memoize
def fibonacci(n):
if n in (0, 1):
return n
return fibonacci(n - 1) + fibonacci(n - 2)

def Rfibonacci(n):
if n in (0, 1):
return n
return Rfibonacci(n - 1) + Rfibonacci(n - 2)

def Sfibonacci(n):
if n in (0, 1):
return n
return Sfibonacci(n - 1) + Sfibonacci(n - 2)
Sfibonacci= memoize(Sfibonacci)

def fib(n):
if n in (0, 1):
return n
i0= 1
i1= 2
lim= n/2 - 1
i= 0
while i < lim:
i+= 1
i0, i1= i0 + i1, i0 + i1 + i1
return (i0, i1)[n & 1]

if __name__ == '__main__':
from timeit import Timer
t = Timer("fib(10)", "from __main__ import fib")
print "Unwrapped, using fib: %.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)
t = Timer("fibonacci(10)", "from __main__ import fibonacci")
print "Fibonacci, using @memoize: %.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)
t = Timer("Rfibonacci(10)", "from __main__ import Rfibonacci")
print "Raw Fibonacci: %.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)
t = Timer("Sfibonacci(10)", "from __main__ import Sfibonacci")
print "Fibonacci with old deco: %.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000)
'''
Results from the above using Windows XP 1.6MHz Pentium 4 with Python 2.4a

C:\Python24\python.exe tFib.py
Unwrapped, using fib: 4.81 usec/pass
Fibonacci, using @memoize: 2.81 usec/pass
Raw Fibonacci: 213.70 usec/pass
Fibonacci with old deco: 2.84 usec/pass

Results, using Python 2.3.3
python -u "tFib.py"
Unwrapped, using fib: 7.75 usec/pass
Raw Fibonacci: 255.84 usec/pass
Fibonacci with old deco: 3.23 usec/pass
Exit code: 0

'''
 
J

Jeff Shannon

Dan said:
If I understand correctly, they'd be useful for anything where you'd
now use the syntax

function = decorator(function)

Many of the uses I'm seeing proposed for decorators would fit better
into the current syntax:

def func( ... )
pass
dosomething(func)

In other words, they're not specifically changing the behavior of the
function, but they are using the (name and/or address of the) function
in some other context. The modification of the function (or method)
itself is an unnecessary side effect.

Whether this distinction matters is obviously a purely aesthetic matter,
since obviously the decorator can rebind the name to the original
function object instead of a wrapping-function object. But still, it
does suggest that this concept is indeed being put to multiple
orthogonal purposes.

Jeff Shannon
Technician/Programmer
Credit International
 
C

Colin J. Williams

Jeff said:
Many of the uses I'm seeing proposed for decorators would fit better
into the current syntax:

def func( ... )
pass
dosomething(func)

In other words, they're not specifically changing the behavior of the
function, but they are using the (name and/or address of the) function
in some other context. The modification of the function (or method)
itself is an unnecessary side effect.
It seems to me that PEP 318 is intended to be transformational, as in
Dan Bishop's examples.

The purpose is to flag the transformation at the head of the function
declaration, ie. to give notice to the reader that the function is not
what it seems to be.

One of the issues is whether the flagging should occur before or after
the function signature. I can see some merit in the flagging and feel
that it better after the signature, where the function name is, in a
sense known.

Another issue is whether a single character or a keyword is used to
flag the 'decorator'. One post suggested that the pie would be better
used as a replacement for self.

There is also the question of whether 'decorator', a term which is used
in everyday langage as indicating adornment, is the best choice for a
transformation.
Whether this distinction matters is obviously a purely aesthetic matter,
since obviously the decorator can rebind the name to the original
function object instead of a wrapping-function object. But still, it
does suggest that this concept is indeed being put to multiple
orthogonal purposes.
Jeff Shannon
Technician/Programmer
Credit International
Colin W.
 
A

Andrew Durdin

There is also the question of whether 'decorator', a term which is used
in everyday langage as indicating adornment, is the best choice for a
transformation.

def deco(f):
print f.__name__, "is now pretty!"
return f

@deco
def ugly_function():
pass
 
M

Mel Wilson

def deco(f):
print f.__name__, "is now pretty!"
return f

def deco (f):
print f.__name__, "is now pretty!"
def t (*s,**k):
print """\
I'm so pretty, oh so pretty!
I'm so pretty and witty and bright!
"""
return t
@deco
def ugly_function():
pass


Throw in an f(*s,**k) there if you really feel you must.


Regards. Mel.


Portions copyright by Stephen Sondheim or somebody.
 

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,982
Messages
2,570,186
Members
46,742
Latest member
AshliMayer

Latest Threads

Top