Python decorator mini-study (part 1 of 3)

Introduction

On quite a few occasions I would find myself coding Python object initialiser methods where I would manually copy the method parameters to object attributes in order to make use of them later (see e.g. TheHardWay::__init__(), lines 13 – 17 below).

This is an unnecessary laborious, error-prone and unpythonic activity :-) In this article I hence present an alternative and more pythonic solution: a function decorator that copies initialiser method parameters to object attributes (see lines 19 – 24 below for an example showing how that decorator is used).

Furthermore, this article shows how to devise Python function decorators that require additional data (beyond the function that is to be decorated) in order to be useful.

Last but not least, the decorator to be shown in (part 3 of) this article is most useful in conjunction with initialiser methods but there is nothing to prevent you from using it with any object method.

Warm-up exercise

Again, the code section below (lines 13 – 17) shows how to copy the parameters to object attributes in a manual fashion.

 1  #!/usr/bin/env python
 2  """Demo code for initialiser method decorator"""
 3
 4  # Copyright: (c) 2006 Muharem Hrnjadovic
 5  # created: 15/10/2006 11:21:01
 6  __version__ = "$Id$"
 7  # $HeadURL $
 8
 9  import pprint as PP, sys
10  from p2adeco import Params2attribs
11
12  class TheHardWay(object):
13      def __init__(self, arg_a, arg_b, arg_c):
14          self.arg_a = arg_a
15          self.arg_b = arg_b
16          self.arg_c = arg_c
17          # .. now do whatever initialisation is required ..

The alternative solution below uses a python function decorator (on line 19) which is given a (possibly empty) tuple of parameter names for which no copying should occur.
In this particular example we don’t want arg_3 to be copied and hence specify it in the tuple passed to the decorator’s object initialiser.

Please note also that the decorator is capable of handling embedded parameter lists (like e.g. parameters arg_2 and arg_3 which are passed to the decorated initialiser via such a list (on line 20)).

18  class CoolApproach(object):
19      @Params2attribs(('arg_3',))
20      def __init__(self, arg_1, (arg_2, arg_3), arg_4, arg_5):
21          # .. at this point all parameters except for 'arg_3' have been
22          # copied to object attributes
23          # .. now do whatever initialisation is required ..
24          print ">> In initialiser, self.arg_1 = '%s'" % self.arg_1

Finally, the block below merely instantiates a CoolApproach object (line 27) in order to demonstrate the described decorator behaviour. Line 28 sorts the attributes of the newly instantiated object for clarity.

25  if __name__ == '__main__':
26      embedded_params = ('E1', 'E2')
27      obj = CoolApproach('aa', embedded_params, 22, 3.33)
28      sorted_attributes = sorted(obj.__dict__.iteritems())
29      sys.stdout.write("\\nObject attributes:\\n%s\\n" %
30                       PP.pformat(sorted_attributes, indent=4, width=60))

When running the code above the following output results:

>> In initialiser, self.arg_1 = 'aa'

Object attributes:
[   ('arg_1', 'aa'),
    ('arg_2', 'E1'),
    ('arg_4', 22),
    ('arg_5', 3.3300000000000001)]

From the output we can see that:

  1. the CoolApproach initialiser method was entered (print statement on line 24)
  2. all the desired initialiser parameters were copied to attributes of the respective object (print statement on lines 29 – 30)

Please note also that arg_3 is not listed as an attribute in the output above indicating that the decorator is paying attention to the parameter exclusion list passed to it.

Conclusion

This is the end of part one, the next part of this article introduces the Python decorator mechanics needed for the final part (dissecting the Params2attribs decorator)

About these ads

8 thoughts on “Python decorator mini-study (part 1 of 3)

  1. Pingback: Plugging Python decorators « Ozone

  2. The comment below pretty much destroys my example :-) but the one-liner at the end is so cool that I felt I had to share this with others.

    From http://programming.reddit.com/info/mxcv/comments/cn1zo

    This is NOT a good use for decorators. Typing few extra lines is not important because you do it only once.

    If the argument list is too long, requiring too many assignments lines, use a parameter object instead.

    If some automation is needed, I would use this:

    def __init__(self, **kw):

    self.__dict__.update(kw)

  3. Re comment #2 – the self.__dict__.update(kw) method is ideal for a constructor with lots of arguments that all are being set to member variables. I still like your decorator approach better for some applications in that you don’t lose the function calling signature (eg, when you accept **kw there’s no way for you (or your IDE) to know just by looking at the function what arguments it takes) and you have the option of ignoring one or two arguments and setting the rest. I still value the conciseness of the decorator, even if it is only saving 3 or 4 lines of code…

  4. Like a lot of programming issues, the merit of this particular application of decorators is largely a matter of personal/group opinion. IMHO, it’s interesting but not a good idea, since it obscures what’s going on and saving a few lines is not worth the loss of readibility to other programmers (remember that much of software development involves maintenance of other people’s code). So as cool as it is, I’d probably avoid it. It’s clever, yes, too clever -:)

    I think there are some other really interesting applications of decorators, such as decorators for memoization and logging the entry, parameters, and return values of functions (great for debugging!). I’d bet that are interesting decorators for RPCifying functions too.

  5. Pingback:  Rizwan Mansuri's Blog 

  6. Barack Obama’s book, “The Audacity of Hope,” provides a appealing title. It has a taste of bravery mixed confidently. You’ll find nothing Pollyanna concerning this. I might not support every part he says, but he’s our president, and for me, he inspires belief. That may do more for a region than any amount of backroom deals. Hope gives us energy, and energy sustains us through trying times. Boy, we’ve had them. I’m from West Texas, and I did not vote for Bush. When McCain ran against Obama, I was a citizen of Arizona, but I gave audacious hope a chance. The fight for progress and laying the foundations of prosperity is not over. I have seen the quips of those that don’t believe Obama is able to do it. But step back a second. Would anyone have all of us fail just to tarnish the star of an incumbent for whom they didn’t vote? Trying to keep our priorities straight, let’s work together with this president and build our future.

  7. Pingback: python tut - SoftwareBuzzer.com

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s