Design Considerations¶
options
is not intended to replace every class’s or method’s
parameter passing mechanisms–just the most highly-optioned
ones that multiplex a package’s functionality to a range of use
cases. These are generally the highest-level, most outward-facing
classes, objects, and APIs. They will generally have at
least four configuration variables (e.g. kwargs used to create,
configure, and define each instance).
In general, classes will define a set of methods that are “outwards
facing”–methods called by external code when consuming the class’s
functionality. Those methods should generally expose their options through
**kwargs
, creating a local variable (say opts
) that represents the sum
of all options in use–the full stack of call, instance, and class options,
including any present magical interpretations.
Internal class methods–the sort that are not generally called by external
code, and that by Python convention are often prefixed by an underscore
(_
)–these generally do not need **kwargs
. They should accept their
options as a single variable (say opts
again) that the externally-facing
methods will provide.
For example, if options
didn’t provide the nice formatting
function attrs
, we might have designed our own:
def _attrs(self, opts):
nicekeys = [ k for k in opts.keys() if not k.startswith('_') ]
return ', '.join([ "{}={}".format(k, repr(opts[k])) for k in nicekeys ])
def draw(self, **kwargs):
opts = self.options.push(kwargs)
print(self._attrs(opts))
draw()
, being the outward-facing API, accepts general arguments and
manages their stacking (by push``ing ``kwargs
onto the instance options).
When the internal _attrs()
method is called, it is handed a pre-digested
opts
package of options.
A nice side-effect of making this distinction: Whenever you see a method with
**kwargs
, you know it’s outward-facing. When you see a method with just
opts
, you know it’s internal.
Objects defined with options
make excellent “callables.”
Define the __call__
method, and you have a very nice analog of
function calls.
options
has broad utility, but it’s not for every class or
module. It best suits high-level front-end APIs that multiplex lots
of potential functionality, and wish/need to do it in a clean/simple
way. Classes for which the set of instance variables is small, or
functions/methods for which the set of known/possible parameters
is limited–these work just fine with classic Python calling
conventions. For those, options
is overkill. “Horses for courses.”