docs: def unique_iter(src, key=None):
      """Yield unique elements from the iterable, *src*, based on *key*,
      in the order in which they first appeared in *src*.
  
      >>> repetitious = [1, 2, 3] * 10
      >>> list(unique_iter(repetitious))
      [1, 2, 3]
  
      By default, *key* is the object itself, but *key* can either be a
      callable or, for convenience, a string name of the attribute on
      which to uniqueify objects, falling back on identity when the
      attribute is not present.
  
      >>> pleasantries = ['hi', 'hello', 'ok', 'bye', 'yes']
      >>> list(unique_iter(pleasantries, key=lambda x: len(x)))
      ['hi', 'hello', 'bye']
      """
      if not is_iterable(src):
          raise TypeError('expected an iterable, not %r' % type(src))
      if key is None:
          key_func = lambda x: x
      elif callable(key):
          key_func = key
      elif isinstance(key, basestring):
          key_func = lambda x: getattr(x, key, x)
      else:
          raise TypeError('"key" expected a string or callable, not %r' % key)
      seen = set()
      for i in src:
          k = key_func(i)
          if k not in seen:
              seen.add(k)
              yield i
      return