* Parameters class: a way to look for parameters in definite search order

across functions, so that these parameters do not have to be passed in
  kitchen-sink manner.
  WARNING: STILL UNDER TESTING.
master
wirawan 15 years ago
parent 3701e5655a
commit ffd21689be
  1. 144
      sugar.py

@ -1,6 +1,6 @@
#!/usr/bin/ipython -pylab #!/usr/bin/ipython -pylab
# #
# $Id: sugar.py,v 1.3 2010-02-19 18:42:15 wirawan Exp $ # $Id: sugar.py,v 1.4 2010-05-28 18:47:56 wirawan Exp $
# #
# Created: 20100121 # Created: 20100121
# Wirawan Purwanto # Wirawan Purwanto
@ -63,3 +63,145 @@ def dict_join(*dicts):
return rslt return rslt
class Parameters(object):
"""A standardized way to define and/or pass parameters (with possible
default values) among routines.
This provides a very flexible lookup scheme for a parameter with a given name.
It scans through the namescopes (dicts) in a deterministic order, returning
the first one found.
This, hopefully, gets rid of kitchen-sink parameter passing, at least from
programmer's point of view.
WARNING: This object is derived object instead of python dict, so as to avoid
messing with standard dict names.
Names reserved by this class begin and end with an underscore.
Names reserved by python begin and end with two underscores.
So, avoid specifying parameters with both leading and trailing underscores.
Some uses:
def stuff(params=None, **kwparams):
# `params' defines the standard way of passing parameters, which is
# via a Parameters object.
# `kwparams' determine a quick way of temporarily overriding a parameter
# value.
prm = Parameters(kwparams, params, global_defaults)
for step in prm.steps:
...
Reserved members:
* _no_null_ = (True/False, default False) look for non-null values in all
the parameter lists until one is found.
* _list_ = (list) the list of parameter dicts to search from.
* _prm_ = (dict) the most overriding list of parameters.
"""
def __init__(self, *_override_dicts_, **_opts_):
"""
Again, keyword arguments passed here will become the most overriding options.
"""
prm = _opts_
self.__dict__["_no_null_"] = ifelse(_opts_.get("_no_null_"), True, False)
self.__dict__["_prm_"] = prm
paramlist = (prm,) + _override_dicts_ #+ tuple(deflist))
self.__dict__["_list_"] = [ p for p in paramlist if p != None ]
def __getattr__(self, key):
"""Allows options to be accessed in attribute-like manner, like:
opt.niter = 3
instead of
opt['niter'] = 3
"""
if self._no_null_:
for ov in self._list_:
if key in ov and ov[key] != None: return ov[key]
else:
for ov in self._list_:
if key in ov: return ov[key]
# Otherwise:
return object.__getattribute__(self, key)
def __setattr__(self, key, value):
"""This method always sets the value on the object's dictionary.
Values set will override any values set in the input parameter lists."""
self._prm_[key] = value
def __contains__(self, key):
if self._no_null_:
for ov in self._list_:
if key in ov and ov[key] != None: return True
else:
for ov in self._list_:
if key in ov: return True
return False
def __getitem__(self, key):
if self._no_null_:
for ov in self._list_:
if key in ov and ov[key] != None: return ov[key]
else:
for ov in self._list_:
if key in ov: return ov[key]
raise KeyError, "Cannot find parameter `%s'" % key
def __setitem__(self, key, value):
self._prm_[key] = value
# TODO in the future for iterative accesses:
# -- not that essential because we know the name of
# the parameters we want to get:
#def __iter__(self):
# return self._prm_.__iter__
#def _iteritems_(self):
# return self._prm_.iteritems()
def _update_(self, srcdict):
self._prm_.update(srcdict)
def _create_(self, kwparams="_opts_", userparams="opts", *defaults):
"""Creates a new Parameters() object for standardized function-level
parameter lookup.
This routine *must* be called by the function where we want to access these
parameters, and where some parameters are to be overriden via function arguments,
etc.
The order of lookup is definite:
*
class Something(object):
def __init__(self, ...):
self.opts = Parameters()
self.opts.cleanup = True # example
def doit(self, src=None, info=None,
_defaults_=dict(src="source.txt", info="INFO.txt", debug=1),
**_opts_):
# FIXME: use self-introspection to reduce kitchen-sink params here:
p = self.opts._create_()
# ^ This will create an equivalent of:
# Parameters(locals(), _opts_, _opts_.get('opts'), self.opts, _defaults)
"""
# Look up the stack of the calling function in order to retrieve its
# local variables
from inspect import stack
caller = stack()[1][0] # one frame up; element-0 is the stack frame
# local variables will be the first to look for
localvars = caller.f_locals
contexts = [ localvars ]
# then _opts_ excess-keyword parameters (see example of doit() above)
if kwparams in localvars:
_opts_ = localvars[kwparams]
contexts.append(_opts_)
else:
_opts_ = {}
# then opts, an explicitly-defined argument carrying set of parameters
if userparams in localvars:
opts = localvars[userparams]
contexts.append(opts)
else:
opts = {}
if userparams in _opts_:
contexts.append(_opts_[userparams])
# then this own Parameters data will come here:
contexts.append(self)
# then any defaults
contexts += [ d for d in defaults ]
# Now construct the Parameters() class for this calling function:
return Parameters(*contexts)

Loading…
Cancel
Save