[Python-ideas] Proposal for new-style decorators

Christophe Schlick cschlick at gmail.com
Wed Apr 27 04:24:32 CEST 2011


On Tue, Apr 26, 2011 at 8:08 PM, Steven D'Aprano <steve at pearwood.info> wrote:

> I would suggest you also publish this decorator-builder recipe on
> ActiveState's Python cookbook, and see if you get much interest there. It
> might also help to post a link to your recipe to python-list at python.org. You
> certainly should do those things before going to python-dev.

OK, I'm going to try that. Thanks.
>
>> * Are there some pitfalls involved with the use of NSD that I haven't
>> seen? Or are there additional desirable elements that could be easily
>> included?
>
> Have you timed the decorated function using new and old style? If you
> decorator a function with (say) 5 arguments, is there any performance hit to
> your NSD?

Intuitively I would say the the only performance hit would comme from
the fact that the decorator arguments are accessed via self.__dict__
in NSD, while there are available as locals with OSD. I've made some
quick 'timeit' tests. I don't know if this is the kind of timing you
thought about:

#---
from timeit import Timer
from decorator import decorator

# OSD
def old_add_args(a=1, b=2, c=3, d=4, e=5):
  def dummy1(func):
    def dummy2(*args, **keys):
      return a + b + c + d + e + func(*args, **keys)
    return dummy2
  return dummy1

# NSD
@decorator(a=1, b=2, c=3, d=4, e=5)
def new_add_args(self, *args, **keys):
  return self.a + self.b + self.c + self.d + self.e + self.func(*args, **keys)

# Apply OSD
@old_add_args()
def old_test(*args, **keys):
  return sum(*args)

# Apply NSD
@new_add_args()
def new_test(*args, **keys):
  return sum(*args)

# Gentle case: the evaluation of the function is rather long compared
to the time
# needed to fetch the 5 decorator args
old_time = Timer('old_test(range(999))', 'from __main__ import
old_test').timeit()
new_time = Timer('new_test(range(999))', 'from __main__ import
new_test').timeit()
print "Gentle: old = %.3f new = %.3f" % (old_time, new_time)

# Worst case: the evaluation of the function is negligible compared to the time
# needed to get the 5 decorators args.
old_time = Timer('old_test(range(1))', 'from __main__ import old_test').timeit()
new_time = Timer('new_test(range(1))', 'from __main__ import new_test').timeit()
print "Worst: old = %.3f new = %.3f" % (old_time, new_time)
#---

Here are the timings obtained on my notebook:

Gentle: old = 45.983 new = 46.377
Worst: old = 4.043 new = 5.127

which seems to confirm that the overhead mainly comes from the
'self.xxx' fetch, and it pretty negligible when heavy computation is
performed.

CS



More information about the Python-ideas mailing list