acting on items passed to a method via a dictiomary

Alex Martelli aleaxit at yahoo.com
Sun Oct 17 17:13:27 CEST 2004


Donnal Walter <donnal at donnal.net> wrote:

> The following method is defined in one of my classes:
> 
>      def setup(self, items={}):
>          """perform setup based on a dictionary of items"""
>          if 'something' in items:
>              value = items['something']
>              # now do something with value
>          if 'another' in items:
>              value = items['another']
>              # do something else with value
>          if 'spam' in items:
>              value = items['spam']
>              # do yet another thing with value
>          if 'eggs' in items:
>              value = items['eggs']
>              # and so on

The various 'do something', 'do something else', etc, pieces of code,
need to be factored out into methods or functions reachable on the basis
of the related names, 'something', 'another', etc.  Depending on exacly
what the various 'somethings' are that you want to do, you might be
happier with placing them in other methods of this class with names
constructed in a systematic way from the related names (this makes it
feasible for subclasses to override some part of them) or local
functions in a dictionary (if you're keen to stop subclasses from
overriding only some part of this processing).

Normally one does want to allow subclasses more flexibility rather than
less (extended variants of the "Template Method" design pattern are very
abundant in the Python standard library, exactly because of this), so,
you might want to do something like...:

class Base(object):

    crucial_names = 'something another spam eggs and so on'.split()

    def method_name(self, crucial_name): return 'handle_'+crucial_name

    def handle_something(self, value): print 'something is', value
    def handle_eggs(self, value): print 'over easy for', value
    # other handle_... deliberately omitted, default to no-op

    def setup(self, items={}):
        for crucial_name in self.crucial_names:
            if crucial_name in items:
                metname = self.method_name(crucial_name)         
                method = getattr(self, metname, None)
                if method: method(items[crucial_name])

This lets a subclass alter the list of "crucial names" (via the highly
Pythonic concept of "data override"), as well as the processing to be
performed for each "crucial name", and even the way a method name is
constructed from the "crucial name".  Of course there are many design
decisions here that you may want to tweak or reverse, depending on your
exact need -- e.g., it may be that in your case a missing method should
not be a plain no-op but a serious error warranting an exception.

Do notice a little detail: it's typically important that the method_name
method 'augment' the crucial_name argument in such a way that the
resulting string cannot possibly be a Python keyword.  For example, if
you had 'def method_name(self, crucial_name): return crucial_name' the
handling of crucial name 'and' might be bothersome, since it's a Python
keyword; if you used, e.g., "return 'a'+crucial_name", a subclass would
have similar problems if it wanted to have 'nd' as a crucial name
(unless it overrode method_name and reimplemented all the existing
handling methods, probably not worth the bother)... it would be feasible
to work around such problems, but it's better to design method_name so
as to avoid the problems arising in the first place!-)


Alex



More information about the Python-list mailing list