Set Attributes in bulk and optionally discard others.

    Sets each Attribute in turn (modifying it in place if possible if it
    is already present) and optionally discarding all other Attributes
    not explicitly set. This function yields much greater performance
    than the required individual calls to ``set_attribute``,
    ``set_attribute_string``, ``set_attribute_string_array`` and
    ``del_attribute`` put together.

    .. versionadded:: 0.2

    Parameters
    ----------
    target : Dataset or Group
        Dataset or Group to set the Attributes of.
    attributes : dict
        The Attributes to set. The keys (``str``) are the names. The
        values are ``tuple`` of the Attribute kind and the value to
        set. Valid kinds are ``'string_array'``, ``'string'``, and
        ``'value'``. The values must correspond to what
        ``set_attribute_string_array``, ``set_attribute_string`` and
        ``set_attribute`` would take respectively.
    discard_others : bool, optional
        Whether to discard all other Attributes not explicitly set
        (default) or not.

    See Also
    --------
    set_attribute
    set_attribute_string
    set_attribute_string_array

def set_attributes_all(target, attributes, discard_others=True):
    """ Set Attributes in bulk and optionally discard others.

    Sets each Attribute in turn (modifying it in place if possible if it
    is already present) and optionally discarding all other Attributes
    not explicitly set. This function yields much greater performance
    than the required individual calls to ``set_attribute``,
    ``set_attribute_string``, ``set_attribute_string_array`` and
    ``del_attribute`` put together.

    .. versionadded:: 0.2

    Parameters
    ----------
    target : Dataset or Group
        Dataset or Group to set the Attributes of.
    attributes : dict
        The Attributes to set. The keys (``str``) are the names. The
        values are ``tuple`` of the Attribute kind and the value to
        set. Valid kinds are ``'string_array'``, ``'string'``, and
        ``'value'``. The values must correspond to what
        ``set_attribute_string_array``, ``set_attribute_string`` and
        ``set_attribute`` would take respectively.
    discard_others : bool, optional
        Whether to discard all other Attributes not explicitly set
        (default) or not.

    See Also
    --------
    set_attribute
    set_attribute_string
    set_attribute_string_array

    """
    attrs = target.attrs
    existing = dict(attrs.items())
    # Generate special dtype for string arrays.
    if sys.hexversion >= 0x03000000:
        str_arr_dtype = h5py.special_dtype(vlen=str)
    else:
        str_arr_dtype = dtype=h5py.special_dtype(vlen=unicode)
    # Go through each attribute. If it is already present, modify it if
    # possible and create it otherwise (deletes old value.)
    for k, (kind, value) in attributes.items():
        if kind == 'string_array':
            attrs.create(k, [convert_to_str(s) for s in value],
                         dtype=str_arr_dtype)
        else:
            if kind == 'string':
                value = np.bytes_(value)
            if k not in existing:
                attrs.create(k, value)
            else:
                try:
                    if value.dtype == existing[k].dtype \
                            and value.shape == existing[k].shape:
                        attrs.modify(k, value)
                except:
                    attrs.create(k, value)
    # Discard all other attributes.
    if discard_others:
        for k in set(existing) - set(attributes):
            del attrs[k]