You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
207 lines
6.7 KiB
207 lines
6.7 KiB
#!/usr/bin/ipython -pylab
|
|
#
|
|
# $Id: sugar.py,v 1.4 2010-05-28 18:47:56 wirawan Exp $
|
|
#
|
|
# Created: 20100121
|
|
# Wirawan Purwanto
|
|
#
|
|
# Syntactic sugar for python programming. May not be efficient, but many
|
|
# of these tools are nice for quick-and-dirty programming.
|
|
# Beware of their caveats!
|
|
#
|
|
#
|
|
import sys
|
|
|
|
def ifelse(cond, trueval, *args):
|
|
"""An alternative to python's own ternary operator, but with multiple
|
|
conditions to test (like chained if-else-if-else... which is found in
|
|
e.g. m4 language).
|
|
This is of course only a syntactic sugar with its inefficiency and
|
|
dangers (all expressions are evaluated before choosing which one is to
|
|
select). So, beware!"""
|
|
if cond:
|
|
return trueval
|
|
else:
|
|
i = 0
|
|
while i+1 < len(args):
|
|
if args[i]: return args[i+1]
|
|
i += 2
|
|
|
|
if i < len(args): return args[i]
|
|
|
|
return None # Fallback solution: "None"
|
|
|
|
|
|
if sys.version_info < (2,4):
|
|
def sorted(List):
|
|
rslt = [ L for L in List ] # == L.copy()
|
|
rslt.sort()
|
|
return rslt
|
|
else:
|
|
#print dir(globals())
|
|
sorted = sorted
|
|
|
|
#print dir(globals())
|
|
|
|
def dict_slice(Dict, *keys):
|
|
"""Returns a shallow copy of the subset of a given dict (or an otherwise
|
|
hashable object) with a given set of keys.
|
|
|
|
Example: if d = {'abc': 12, 'def': 7, 'ghi': 32, 'jkl': 98 }
|
|
then dict_slice(d, 'abc', 'ghi') will yield {'abc': 12, 'ghi': 32 }
|
|
"""
|
|
return dict([ (k, Dict[k]) for k in keys ])
|
|
|
|
def dict_join(*dicts):
|
|
"""Join multiple dicts into one, updating duplicate keys from left-to-right
|
|
manner.
|
|
Thus the key from the rightmost dict will take precedence."""
|
|
|
|
rslt = {}
|
|
for d in dicts:
|
|
rslt.update(d)
|
|
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)
|
|
|
|
|
|
|