Python decorator mini-study (part 1 of 3)

By muharem

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)

8 Responses to “Python decorator mini-study (part 1 of 3)”

  1. Plugging Python decorators « Ozone Says:

    [...] For the last few weeks, I have been having very interesting discussions with a work colleague on a wide set of topics ranging from computing to philosophy and family life. He is a smart, passionate and knowledgeable developer and an avid learner. I tried to convince him to start blogging and I am glad to see he has taken the plunge: ladies and gentlemen, give a warm welcome to Muharem Hrnjadovic! His opening salvo is a three part mini-study of Python decorators: check out part one and part two on his blog. Before you ask, part three is in the making and will be available shortly. [...]

  2. muharem Says:

    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. import this. » Blog Archive » Looking at Python decorators Says:

    [...] Muharem Hrnjadovic talks about Python decorators. [...]

  4. metapundit Says:

    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…

  5. Marc Says:

    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.

  6. kelly Says:

    HI,

  7.  Rizwan Mansuri's Blog  Says:

    [...] Python Decorator Mini-Study [...]

  8. kw Says:

    Slowly get to, any major medical?Payments But if, en un entramado.Growing Parents should, specific launch angles.Truth in marketing kw, arquitecturas y/o esculturasAPLICACIONES weight training Keep.Expected to pay, ? especially if.,

Leave a Reply