docs: def instancelist(obj_list, check=False, shared_attrs=None):
      """
      Executes methods and attribute calls on a list of objects of the same type
  
      Bundles a list of object of the same type into a single object.
      The new object contains the same functions as each original object
      but applies them to each element of the list independantly when called.
  
      CommandLine:
          python -m utool.util_dev instancelist
  
      Example:
          >>> # ENABLE_DOCTEST
          >>> from utool.util_dev import *  # NOQA
          >>> import utool as ut
          >>> obj_list = ['hi', 'bye', 'foo']
          >>> self = ut.instancelist(obj_list, check=False)
          >>> print(self)
          >>> print(self.upper())
          >>> print(self.isalpha())
      """
      class InstanceList_(object):
          def __init__(self, obj_list, shared_attrs=None):
              self._obj_list = []
              self._shared_public_attrs = []
              self._example_type = None
  
              if len(obj_list) > 0:
                  import utool as ut
                  self._obj_list = obj_list
  
                  example_obj = obj_list[0]
                  example_type = type(example_obj)
                  self._example_type = example_type
  
                  if shared_attrs is None:
                      if check:
                          attrsgen = [set(dir(obj)) for obj in obj_list]
                          shared_attrs = list(reduce(set.intersection, attrsgen))
                      else:
                          shared_attrs = dir(example_obj)
  
                  #allowed = ['__getitem__']  # TODO, put in metaclass
                  allowed = []
                  self._shared_public_attrs = [
                      a for a in shared_attrs
                      if a in allowed or not a.startswith('_')
                  ]
  
                  for attrname in self._shared_public_attrs:
                      attrtype = getattr(example_type, attrname, None)
                      if attrtype is not None and isinstance(attrtype, property):
                          # need to do this as metaclass
                          setattr(InstanceList_, attrname,
                                  property(self._define_prop(attrname)))
                      else:
                          func = self._define_func(attrname)
                          ut.inject_func_as_method(self, func, attrname)
  
          def __nice__(self):
              if self._example_type is None:
                  typename = 'object'
              else:
                  typename = self._example_type.__name__
              return 'of %d %s(s)' % (len(self._obj_list), typename)
  
          def __repr__(self):
              classname = self.__class__.__name__
              devnice = self.__nice__()
              return '<%s(%s) at %s>' % (classname, devnice, hex(id(self)))
  
          def __str__(self):
              classname = self.__class__.__name__
              devnice = self.__nice__()
              return '<%s(%s)>' % (classname, devnice)
  
          def __getitem__(self, key):
              # TODO, put in metaclass
              return self._map_method('__getitem__', key)
  
          def _define_func(self, attrname):
              import utool as ut
              def _wrapper(self, *args, **kwargs):
                  return self._map_method(attrname, *args, **kwargs)
              ut.set_funcname(_wrapper, attrname)
              return _wrapper
  
          def _map_method(self, attrname, *args, **kwargs):
              mapped_vals = [getattr(obj, attrname)(*args, **kwargs)
                             for obj in self._obj_list]
              return mapped_vals
  
          def _define_prop(self, attrname):
              import utool as ut
              def _getter(self):
                  return self._map_property(attrname)
              ut.set_funcname(_getter, 'get_' + attrname)
              return _getter
  
          def _map_property(self, attrname):
              mapped_vals = [getattr(obj, attrname) for obj in self._obj_list]
              return mapped_vals
      return InstanceList_(obj_list, shared_attrs)