Michael Loritsch wrote:
Jeremy Bowers <je**@jerf.org> wrote: It mostly depends on the contents of the "do something with
value" that you have peppered throughout. I would pull out
the various "do somethings" into methods, but other then that,
if you need the full flexibility of a dict that can contain
anything and you need to do things with arbitrary combinations,
there is little else to do.
<snip>
I agree with Jeremy here that everything depends on what "do
something with value" means.
Thanks again for the additional helpful replies. I am still learning how
to ask (good) questions, and in my original post I was guilty of
abstracting away too many important details.
First of all, I am/was aware of the potential problem associated with
using the default "items={}" in the signature of the setup() method. In
fact, my own code used:
def setup(self, items):
where items is generated by **kwds somewhere else. Without thinking,
though, I wrote "items = {}" in my posting merely to indicate that items
might be an empty dictionary in some cases, which several rightly
pointed out could lead to "strange default value behavior".
More importantly, I left out a couple of crucial features of the setup()
method itself. I did so deliberately because I thought it would add
confusion to what I hoped was a simple question, but now I see that this
is probably critical information. The first thing I left out is that
setup() is what I call a 'univeral' method. When it is called from the
__init__() method for the instance, setup() is bound to the instance,
but when it is called from the metaclass __init__() method, it is bound
to the class itself. This way, the specification values in *items* can
be passed as class attributes (via dict in the metaclass __init__) or as
keywords collected by the instance __init__(). Moreover, I "decorate"
this method/function as being "universal" from within the metaclass
__init__() rather than in the body of the class statement. At first I
thought this was pretty cool, but the only way I could figure out to
call a super setup() from a subclass (since this is a universal method)
is to supply a munged name "_klass.__setup__" for subclasses to call.
This is done in the metaclass as well. It is a hack, but it works.
With regard to factoring out the various 'do something' pieces of
code, I agree that this appears to be the most appropriate way to define
the setup process, and for example it would eliminate the need for
munging the name for subclasses. Indeed I started out this way. But here
is why I ended up putting all the code in one method. First, some of the
items interact (such as logical ORing several different flags). This
could be done in separate methods by using an appropriate instance
attribute, but it is easier to use a local variable to hold the
intermediate result. Second, the order in which some of the setup
functions are performed is significant. Dispatching from a list would
preserve an order, of course, but in this case, it reads better to
define the various functionalities one after another in the body of a
single method. And finally, having separate setup methods for each item
in the specification means that the metaclass would need to decorate
each one of these methods as being "universal". Better here than in the
body of the subclasses (IMHO), but this still adds more complexity than
I had hoped for.
From the standpoint of the API itself, it would be nice if each
subclass could simply define a set of methods "setup_xxxx" where "xxxx"
is the name of a specification to look for in "items". If I can figure
out how to avoid the need for these specs to be handled in a particular
order, it would make this approach much easier. If I end up keeping all
the code in a single method (for each subclass), I will likely adopt
Ville's suggestion (defining dictget(d, key)) to avoid repeating each
key name.
Thanks,
Donnal Walter
Arkansas Children's Hospital
#-----------------------------------------------------------------------------
# Revised: 2004-10-18
# Copyright: (c) 2003-2004 Donnal C. Walter (
http://mindwrapper.org)
# License: BSD License (
http://www.mindwrapper.org/license)
#-----------------------------------------------------------------------------
class universal(object):
"""universal method: binds to either a class or an instance
Thanks to Thomas Heller on comp.lang.python
http://tinyurl.com/51qd or
http://tinyurl.com/51qm
"""
def __init__(self, func):
self.__func = func
def __get__(self, inst, type=None):
if inst is None:
# bind to the class
return self.__BoundMethod(self.__func, type)
else:
# bind to the instance
return self.__BoundMethod(self.__func, inst)
class __BoundMethod:
# Helper class.
def __init__(self, func, first):
self.__func = func
self.__first = first
def __call__(self, *args, **kwds):
return self.__func(self.__first, *args, **kwds)
class Item:
class __metaclass__(type):
def __init__(cls, name, bases, dict):
type.__init__(cls, name, bases, dict)
n = 'setup' # declarations handler
if n in dict: # if class def has this name
fnc = dict.pop(n) # retrieve the function
ufnc = universal(fnc) # make into universal method
setattr(cls, n, ufnc) # for class or instance
n = '_' + cls.__name__ + '__' + n # and local name
setattr(cls, n, ufnc) # for subclasses to call
cls.setup(dict) # then execute for this class
_args = []
def __init__(self, parent=None, name='', *refs, **kwds):
self._parent = parent # node is in a parent
self._name = name # node is named
if refs:
self._refs = refs # cache refs if present
# overrides class refs
# setup can override
self.setup(kwds) # handle specifications
def setup(self, kwds):
if 'refs' in kwds:
self._refs = aslist(kwds.pop('refs'))
return kwds