<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def set_options(cls, obj, options=None, backend=None, **kwargs):
 """ Pure Python function for customize HoloViews objects in terms of their style, plot and normalization options. The options specification is a dictionary containing the target for customization as a {type}.{group}.{label} keys. An example of such a key is 'Image' which would customize all Image components in the object. The key 'Image.Channel' would only customize Images in the object that have the group 'Channel'. The corresponding value is then a list of Option objects specified with an appropriate category ('plot', 'style' or 'norm'). For instance, using the keys described above, the specs could be: {'Image:[Options('style', cmap='jet')]} Or setting two types of option at once: {'Image.Channel':[Options('plot', size=50), Options('style', cmap='Blues')]} Relationship to the %%opts magic This function matches the functionality supplied by the %%opts cell magic in the IPython extension. In fact, you can use the same syntax as the IPython cell magic to achieve the same customization as shown above: from holoviews.util.parser import OptsSpec set_options(my_image, OptsSpec.parse("Image (cmap='jet')")) Then setting both plot and style options: set_options(my_image, OptsSpec.parse("Image [size=50] (cmap='Blues')")) """

        # Note that an alternate, more verbose and less recommended
        # syntax can also be used:

        # {'Image.Channel:{'plot':  Options(size=50),
        #                  'style': Options('style', cmap='Blues')]}
        options = cls.merge_options(Store.options(backend=backend).groups.keys(), options, **kwargs)
        spec, compositor_applied = cls.expand_compositor_keys(options)
        custom_trees, id_mapping = cls.create_custom_trees(obj, spec)
        cls.update_backends(id_mapping, custom_trees, backend=backend)
        
        # Propagate ids to the objects
        not_used = []
        for (match_id, new_id) in id_mapping:
            applied = cls.propagate_ids(obj, match_id, new_id, compositor_applied+list(spec.keys()), backend=backend)
            if not applied:
                not_used.append(new_id)

        # Clean up unused custom option trees
        for new_id in set(not_used):
            cleanup_custom_options(new_id)

        return obj