def __setattr__(self, key, value): """Add a new attribute to the header. Parameters ---------- key : str Attribute to set. value : any Value to assign to the attribute. Could be an immutable object (scalar, tuple, dict, str), or a vector-like CArray. Lists are automatically converted to vector-like CArrays. """ # We store lists as CArrays to facilitate indexing value = CArray(value) if is_list(value) else value # Make sure we store arrays as vector-like value = value.ravel() if isinstance(value, CArray) else value super(CDatasetHeader, self).__setattr__(key, value) # Make sure that input writable attributes are consistent if is_writable(self, key): self._validate_params()
def set_state(self, state_dict, copy=False): """Sets the object state using input dictionary. Only readable attributes of the class, i.e. PUBLIC or READ/WRITE or READ ONLY, can be set. If possible, a reference to the attribute to set is assigned. Use `copy=True` to always make a deepcopy before set. Parameters ---------- state_dict : dict Dictionary containing the state of the object. copy : bool, optional By default (False) a reference to the attribute to assign is set. If True or a reference cannot be extracted, a deepcopy of the attribute is done first. """ def copy_attr(attr_tocopy): from copy import deepcopy return deepcopy(attr_tocopy) for param_name in state_dict: # Extract the value of the attribute to set param_value = state_dict[param_name] # Support for recursive setting, e.g. -> kernel.gamma param_name = param_name.split('.', 1) # Attributes to set in this function must be readable # PUBLIC, READ/WRITE and READ ONLY accessibility is checked if not is_readable(self, param_name[0]): raise AttributeError( "can't set `{:}`, must be readable.".format(param_name[0])) attr0 = param_name[0] if hasattr(self, attr0): # 1 level set or multiple sublevels set? if len(param_name) == 1: # Set attribute directly # If writable (public or property with setter) if is_writable(self, attr0): # Use main `.set` self.set(attr0, param_value, copy=copy) continue # Attribute set, go to next one else: # Maybe is read-only (property with only getter)? # If exists, set the protected attribute if has_protected(self, attr0): attr0 = as_protected(attr0) setattr(self, attr0, copy_attr(param_value) if copy is True else param_value) continue # Attribute set, go to next one else: # Start recursion on sublevels # Call `.set_state` for the next level of current attribute getattr(self, attr0).set_state( {param_name[1]: param_value}, copy) continue # Attribute set, go to next one # Attribute not found, raise AttributeError raise AttributeError( "'{:}', or any of its attributes, has attribute '{:}'" "".format(self.__class__.__name__, attr0))
def set(self, param_name, param_value, copy=False): """Set a parameter of the class. Only writable attributes of the class, i.e. PUBLIC or READ/WRITE, can be set. The following checks are performed before setting: - if `param_name` is an attribute of current class, set directly; - else, iterate over __dict__ and look for a class attribute having the desired parameter as an attribute; - else, if attribute is not found on the 2nd level, raise AttributeError. If possible, a reference to the attribute to set is assigned. Use `copy=True` to always make a deepcopy before set. Parameters ---------- param_name : str Name of the parameter to set. param_value : any Value to set for the parameter. copy : bool By default (False) a reference to the parameter to assign is set. If True or a reference cannot be extracted, a deepcopy of the parameter value is done first. """ def copy_attr(attr_tocopy): from copy import deepcopy return deepcopy(attr_tocopy) # Support for recursive setting, e.g. -> kernel.gamma param_name = param_name.split('.') attr0 = param_name[0] if hasattr(self, attr0): # Level 0 set or multiple sublevels set? if len(param_name) == 1: # Set attribute directly # Level 0 attribute must be writable # PUBLIC and READ/WRITE accessibility is checked if not is_writable(self, attr0): raise AttributeError( "can't set `{:}`, must be writable.".format(attr0)) setattr(self, attr0, copy_attr(param_value) if copy is True else param_value) return else: # Start recursion on sublevels # Level 0 attribute must be accessible (readable) # PUBLIC, READ/WRITE and READ ONLY accessibility is checked if not is_readable(self, attr0): raise AttributeError( "can't set `{:}`, must be accessible.".format(attr0)) sub_param_name = '.'.join(param_name[1:]) # Calling `.set` method of the next sublevel getattr(self, attr0).set(sub_param_name, param_value, copy) return # OLD STYLE SET: recursion on 2 levels only to set a subattribute # The first subattribute found is set... else: # Look for the attribute inside all class attributes for attr_name in self.__dict__: # Extract the current attribute attr = getattr(self, attr_name) # If parameter is an attribute of current attribute set it if hasattr(attr, attr0): # Attributes to set must be writable # PUBLIC and READ/WRITE accessibility is checked if not is_writable(attr, attr0): raise AttributeError( "can't set `{:}`, must be writable.".format(attr0)) setattr(attr, attr0, copy_attr(param_value) if copy is True else param_value) return # Attribute not found, raise AttributeError raise AttributeError( "'{:}', or any of its attributes, has attribute '{:}'" "".format(self.__class__.__name__, attr0))