Package mvpa :: Package misc :: Module attributes

[hide private]
[frames] | no frames]

# Source Code for Module mvpa.misc.attributes

      1  # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
      2  # vi: set ft=python sts=4 ts=4 sw=4 et:
      3  ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
      4  #
      5  #   See COPYING file distributed along with the PyMVPA package for the
      6  #   copyright and license terms.
      7  #
      8  ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
      9  """Module with some special objects to be used as magic attributes with
     10  dedicated containers aka. `Collections`.
     11  """
     12
     13  __docformat__ = 'restructuredtext'
     14
     15
     16  from mvpa.misc.exceptions import UnknownStateError
     17
     18  if __debug__:
     19      from mvpa.base import debug
     20
     21
     22
     23  ##################################################################
     24  # Various attributes which will be collected into collections
     25  #
     26 -class CollectableAttribute(object):
     27      """Base class for any custom behaving attribute intended to become
     28      part of a collection.
     29
     30      Derived classes will have specific semantics:
     31
     32      * StateVariable: conditional storage
     33      * AttributeWithUnique: easy access to a set of unique values
     34        within a container
     35      * Parameter: attribute with validity ranges.
     36
     37        - ClassifierParameter: specialization to become a part of
     38          Classifier's params collection
     39        - KernelParameter: --//-- to become a part of Kernel Classifier's
     40          kernel_params collection
     41
     42      Those CollectableAttributes are to be groupped into corresponding
     43      collections for each class by statecollector metaclass, ie it
     44      would be done on a class creation (ie not per each object)
     45      """
     46
     47      _instance_index = 0
     48
     49 -    def __init__(self, name=None, doc=None, index=None):
     50          if index is None:
     51              CollectableAttribute._instance_index += 1
     52              index = CollectableAttribute._instance_index
     53          self._instance_index = index
     54          self.__doc__ = doc
     55          self.__name = name
     56          self._value = None
     57          self._isset = False
     58          self.reset()
     59          if __debug__:
     60              debug("COL",
     61                    "Initialized new collectable #%d:%s" % (index,name) + `self`)
     62
     63
     64      # Instead of going for VProperty lets make use of virtual method
     65 -    def _getVirtual(self):
     66          return self._get()
     67
     68
     69 -    def _setVirtual(self, value):
     70          return self._set(value)
     71
     72
     73 -    def _get(self):
     74          return self._value
     75
     76
     77 -    def _set(self, val):
     78          if __debug__:
     79              # Since this call is quite often, don't convert
     80              # values to strings here, rely on passing them
     81              # withing msgargs
     82              debug("COL",
     83                    "Setting %(self)s to %(val)s ",
     84                    msgargs={'self':self, 'val':val})
     85          self._value = val
     86          self._isset = True
     87
     88
     89      @property
     90 -    def isSet(self):
     91          return self._isset
     92
     93
     94 -    def reset(self):
     95          """Simply reset the flag"""
     96          if __debug__ and self._isset:
     97              debug("COL", "Reset %s to being non-modified" % self.name)
     98          self._isset = False
     99
    100
    101      # TODO XXX unify all bloody __str__
    102 -    def __str__(self):
    103          res = "%s" % (self.name)
    104          if self.isSet:
    105              res += '*'          # so we have the value already
    106          return res
    107
    108
    109 -    def _getName(self):
    110          return self.__name
    111
    112
    113 -    def _setName(self, name):
    114          """Set the name of parameter
    115
    116          .. note::
    117            Should not be called for an attribute which is already assigned
    118            to a collection
    119          """
    120          if name is not None:
    121              if isinstance(name, basestring):
    122                  if name[0] == '_':
    123                      raise ValueError, \
    124                            "Collectable attribute name must not start " \
    125                            "with _. Got %s" % name
    126              else:
    127                  raise ValueError, \
    128                        "Collectable attribute name must be a string. " \
    129                        "Got %s" % `name`
    130          self.__name = name
    131
    132
    133      # XXX should become vproperty?
    134      # YYY yoh: not sure... someone has to do performance testing
    135      #     to see which is more effective. My wild guess is that
    136      #     _[gs]etVirtual would be faster
    137      value = property(_getVirtual, _setVirtual)
    138      name = property(_getName) #, _setName)
    139
    140
    141
    142  # XXX think that may be discard hasunique and just devise top
    143  #     class DatasetAttribute
    144 -class AttributeWithUnique(CollectableAttribute):
    145      """Container which also takes care about recomputing unique values
    146
    147      XXX may be we could better link original attribute to additional
    148      attribute which actually stores the values (and do reverse there
    149      as well).
    150
    151      Pros:
    152        * don't need to mess with getattr since it would become just another
    153          attribute
    154
    155      Cons:
    156        * might be worse design in terms of comprehension
    157        * take care about _set, since we shouldn't allow
    158          change it externally
    159
    160      For now lets do it within a single class and tune up getattr
    161      """
    162
    163 -    def __init__(self, name=None, hasunique=True, doc="Attribute with unique"):
    164          CollectableAttribute.__init__(self, name, doc)
    165          self._hasunique = hasunique
    166          self._resetUnique()
    167          if __debug__:
    168              debug("UATTR",
    169                    "Initialized new AttributeWithUnique %s " % name + `self`)
    170
    171
    172 -    def reset(self):
    173          super(AttributeWithUnique, self).reset()
    174          self._resetUnique()
    175
    176
    177 -    def _resetUnique(self):
    178          self._uniqueValues = None
    179
    180
    181 -    def _set(self, *args, **kwargs):
    182          self._resetUnique()
    183          CollectableAttribute._set(self, *args, **kwargs)
    184
    185
    186 -    def _getUniqueValues(self):
    187          if self.value is None:
    188              return None
    189          if self._uniqueValues is None:
    190              # XXX we might better use Set, but yoh recalls that
    191              #     N.unique was more efficient. May be we should check
    192              #     on the the class and use Set only if we are not
    193              #     dealing with ndarray (or lists/tuples)
    194              self._uniqueValues = N.unique(N.asanyarray(self.value))
    195          return self._uniqueValues
    196
    197      uniqueValues = property(fget=_getUniqueValues)
    198      hasunique = property(fget=lambda self:self._hasunique)
    199
    200
    201
    202  # Hooks for comprehendable semantics and automatic collection generation
    203 -class SampleAttribute(AttributeWithUnique):
    204      pass
    205
    206
    207
    208 -class FeatureAttribute(AttributeWithUnique):
    209      pass
    210
    211
    212
    213 -class DatasetAttribute(AttributeWithUnique):
    214      pass
    215
    216
    217
    218 -class StateVariable(CollectableAttribute):
    219      """Simple container intended to conditionally store the value
    220      """
    221
    222 -    def __init__(self, name=None, enabled=True, doc="State variable"):
    223          # Force enabled state regardless of the input
    224          # to facilitate testing
    225          if __debug__ and 'ENFORCE_STATES_ENABLED' in debug.active:
    226              enabled = True
    227          CollectableAttribute.__init__(self, name, doc)
    228          self._isenabled = enabled
    229          self._defaultenabled = enabled
    230          if __debug__:
    231              debug("STV",
    232                    "Initialized new state variable %s " % name + `self`)
    233
    234
    235 -    def _get(self):
    236          if not self.isSet:
    237              raise UnknownStateError("Unknown yet value of %s" % (self.name))
    238          return CollectableAttribute._get(self)
    239
    240
    241 -    def _set(self, val):
    242          if self.isEnabled:
    243              # XXX may be should have left simple assignment
    244              # self._value = val
    245              CollectableAttribute._set(self, val)
    246          elif __debug__:
    247              debug("COL",
    248                    "Not setting disabled %(self)s to %(val)s ",
    249                    msgargs={'self':self, 'val':val})
    250
    251
    252 -    def reset(self):
    253          """Simply detach the value, and reset the flag"""
    254          CollectableAttribute.reset(self)
    255          self._value = None
    256
    257
    258      @property
    259 -    def isEnabled(self):
    260          return self._isenabled
    261
    262
    263 -    def enable(self, value=False):
    264          if self._isenabled == value:
    265              # Do nothing since it is already in proper state
    266              return
    267          if __debug__:
    268              debug("STV", "%s %s" %
    269                    ({True: 'Enabling', False: 'Disabling'}[value], str(self)))
    270          self._isenabled = value
    271
    272
    273 -    def __str__(self):
    274          res = CollectableAttribute.__str__(self)
    275          if self.isEnabled:
    276              res += '+'          # it is enabled but no value is assigned yet
    277          return res
    278