def set_description(cls, descr):
        """Set the feature description.

        This extracts the number of features from the description and checks
        that it matches with the `nfeatures`. If `nfeatures` is None, `nfeatures`
        is set to the extracted value.

        Parameters
        ----------
        descr : dict
            The feature description.

        Raises
        ------
        ValueError
            If the number of features extracted from the description does not
            match `nfeatures` or if `name` isn't of type string.

        Notes
        -----
        Use the `description` to encode action information. The information
        should contain the list of all available feature combinations, the
        name of each feature.

        Examples
        --------

            A description of an action with three possible discrete actions:

            ::

                {
                    "out": {"value": [-0.004]},
                    "in": {"value": [0.004]},
                    "kick": {"value": [-1.0]}
                }

            A description of an action with one possible continuous action with
            name `move`, a value of `*` allows to find the action for every
            feature array. Additional information encodes the feature name together
            with its index into the feature array are given for each higher level
            element of feature array:

            ::

                {
                    "move": {
                        "value": "*",
                        "descr": {
                            "LArm": {"dx": 0, "dy": 1, "dz": 2},
                            "RArm": {"dx": 3, "dy": 4, "dz": 5},
                            "LLeg": {"dx": 6, "dy": 7, "dz": 8},
                            "RLeg": {"dx": 9, "dy": 10, "dz": 11},
                            "Torso": {"dx": 12, "dy": 13, "dz": 14}
                        }
                    }
                }

            Similarly, a continuous state can be encoded as follows, which identifies
            the name of each feature together with its index into the feature array:

            ::

                {
                    "LArm": {"x": 0, "y": 1, "z": 2},
                    "RArm": {"x": 3, "y": 4, "z": 5},
                    "LLeg": {"x": 6, "y": 7, "z": 8},
                    "RLeg": {"x": 9, "y": 10, "z": 11},
                    "Torso": {"x": 12, "y": 13, "z": 14}
                }

            A discrete state can be encoded by identifying the position of each feature:

            ::

                "descr": {
                    "image x-position": 0,
                    "displacement (mm)": 1
                }

            Alternatively, the feature can be identified by a list of features, giving he
            positional description:

            ::

                ["image x-position", "displacement (mm)"]

        """
        nfeatures = None
        if isinstance(descr, dict):
            config = descr.itervalues().next()
            if 'descr' in config:
                nfeatures = sum(len(v) for v in config['descr'].itervalues())
                if cls.nfeatures is not None and not cls.nfeatures == nfeatures:
                    raise ValueError("Dimension mismatch: array described by 'descr' is a vector of length %d,"
                                     " but attribute cls.nfeatures = %d" % (nfeatures, cls.nfeatures))
            elif 'value' in config and not config['value'] == '*':
                nfeatures = len(config['value'])
                if cls.nfeatures is not None and not cls.nfeatures == nfeatures:
                    raise ValueError("Dimension mismatch: array described by 'value' is a vector of length %d,"
                                     " but attribute cls.nfeatures = %d" % (nfeatures, cls.nfeatures))
            else:
                nfeatures = sum(len(v) for v in descr.itervalues())
                if cls.nfeatures is not None and not cls.nfeatures == nfeatures:
                    raise ValueError("Dimension mismatch: 'descr' is a vector of length %d,"
                                     " but attribute cls.nfeatures = %d" % (nfeatures, cls.nfeatures))

        elif isinstance(descr, list):
            nfeatures = len(descr)
            if cls.nfeatures is not None and not cls.nfeatures == nfeatures:
                raise ValueError("Dimension mismatch: 'descr' is a vector of length %d,"
                                 " but attribute cls.nfeatures = %d" % (nfeatures, cls.nfeatures))

        if cls.nfeatures is None:
            cls.nfeatures = nfeatures
        cls.description = descr