Expand values from various attribute types.

    Strings are returned as is.
    Dictionaries are returned with a key string, and an expanded set of values.
    Other iterables are expanded until they flatten out.
    Other items are returned in string format.

    Args:
      obj: The object to expand out.
      parent: The parent object: Used to short-circuit infinite recursion.

    Returns:
      a list of expanded values as strings.

def FanOut(self, obj, parent=None):
    """Expand values from various attribute types.

    Strings are returned as is.
    Dictionaries are returned with a key string, and an expanded set of values.
    Other iterables are expanded until they flatten out.
    Other items are returned in string format.

    Args:
      obj: The object to expand out.
      parent: The parent object: Used to short-circuit infinite recursion.

    Returns:
      a list of expanded values as strings.
    """
    # Catch cases where RDFs are iterable but return themselves.
    if parent and obj == parent:
      results = [utils.SmartUnicode(obj).strip()]
    elif isinstance(obj, (string_types, rdf_structs.EnumNamedValue)):
      results = [utils.SmartUnicode(obj).strip()]
    elif isinstance(obj, rdf_protodict.DataBlob):
      results = self.FanOut(obj.GetValue())
    elif isinstance(obj, (collections.Mapping, rdf_protodict.Dict)):
      results = []
      # rdf_protodict.Dict only has items, not iteritems.
      for k, v in iteritems(obj):
        expanded_v = [utils.SmartUnicode(r) for r in self.FanOut(v)]
        results.append("%s:%s" % (utils.SmartUnicode(k), ",".join(expanded_v)))
    elif isinstance(obj, (collections.Iterable,
                          rdf_structs.RepeatedFieldHelper)):
      results = []
      for rslt in [self.FanOut(o, obj) for o in obj]:
        results.extend(rslt)
    else:
      results = [utils.SmartUnicode(obj).strip()]
    return results