Source code for alogos._utilities.parametrization

from .. import exceptions as _exceptions


[docs]class ParameterCollection: """A collection of parameters with initial values that can be changed by a user. Provided features: - The set of available parameter names is defined once and for all by a dict passed to ``__init__``. - The user can get and set parameter values by dot notation (via ``__setattr__``, e.g. ``param.a = 4``) and bracket notation (via ``__getitem__`` and ``__setitem__``, e.g. ``param['a'] = 4``). - The user can check if a parameter name is included in the collection (via __contains__, e.g. 'a' in param). - If a user tries to get or set an unknown parameter (detected via ``__getattr__``), an error is raised, which lists all available parameters and their current and initial values. Note that there is a deliberate asymmetry between ``__setattr__`` and ``__getattr__`` in Python 3. The former is called on any attribute assignment, but the latter is only called when attribute lookup fails by all other methods. References ---------- - https://docs.python.org/3/reference/datamodel.html - https://docs.python.org/3/library/copy.html - https://ipython.readthedocs.io/en/stable/config/integrating.html """
[docs] def __init__(self, parameter_dict): """Create a parameter collection from a dictionary.""" for name in ["keys", "values", "items"]: if name in parameter_dict: _exceptions.raise_initial_parameter_error(name) self._initial_parameters = parameter_dict self.__dict__.update(parameter_dict)
def __str__(self): """Compute the "informal" string representation of the parameter collection.""" def append_parameters_to_output(lines, pc, indent=0): for key in pc: original_value = pc._initial_parameters[key] current_value = pc[key] if isinstance(current_value, ParameterCollection): line = "{}- {}:".format(" " * indent, key) lines.append(line) append_parameters_to_output(lines, current_value, indent + 2) elif current_value == original_value: line = "{}- {}: {}".format(" " * indent, key, repr(original_value)) lines.append(line) else: line = "{}- {}: {} currently, {} originally".format( " " * indent, key, repr(current_value), repr(original_value) ) lines.append(line) return lines lines = [] lines.append("Parameters:") lines = append_parameters_to_output(lines, self) return "\n".join(lines) def __repr__(self): """Compute the "official" string representation of the parameter collection.""" return "<ParameterCollection object at {}>".format(hex(id(self))) def _repr_pretty_(self, p, cycle): """Provide rich display representation for IPython and Jupyter.""" if cycle: p.text(repr(self)) else: p.text(str(self)) def __contains__(self, item): """Check if a given string is a known parameter name.""" return item in self._initial_parameters.keys() def __getattr__(self, key): """Provide a fallback when default attribute access fails. Notes ----- Properties starting with '__' are silently ignored, so that inspection methods do not raise unnecessary errors, such as those used by Python's built-in help system. """ if not key.startswith("__"): _exceptions.raise_unknown_parameter_error(key, self) def __setattr__(self, key, value): """Enable attribute assignment.""" if key != "_initial_parameters" and key not in self._initial_parameters: _exceptions.raise_unknown_parameter_error(key, self) self.__dict__[key] = value def __getitem__(self, key): """Enable item retrieval.""" if key not in self: _exceptions.raise_unknown_parameter_error(key, self) return self.__dict__[key] def __setitem__(self, key, value): """Enable item assignment via object[key].""" self.__setattr__(key, value) def __iter__(self): """Return an iterator that goes over all parameter names.""" return iter(name for name in self._initial_parameters) def __len__(self): """Compute the number of parameters.""" return len(self._initial_parameters) def __copy__(self): """Create a shallow copy this object.""" current_dict = {key: val for key, val in self.items()} return current_dict
[docs] def keys(self): """Get parameter names.""" return self._initial_parameters.keys()
[docs] def values(self): """Get parameter values.""" return (self.__dict__[key] for key in self._initial_parameters)
[docs] def items(self): """Get parameter names and values.""" return ((key, self.__dict__[key]) for key in self._initial_parameters)
[docs]def get_given_or_default(name, given_parameters, default_parameters): """Get a parameter from a collection of given parameters or otherwise from defaults.""" try: return given_parameters[name] except Exception: return default_parameters[name]