Repository: pandas-dev/pandas

Issue:
MultiIndex smart repr fmt doesn't work well for complex examples
``` python
In [42]: labels = [('foo', 1),
    ...:  ('foo', 2),
    ...:  ('bar', 3),
    ...:  ('bar', 4)]
    ...: pd.MultiIndex.from_tuples(labels)
Out[42]: 
MultiIndex
[foo  1,      2, bar  3,      4]
```

hiding the repeated `foo` and `bar` is nice for small multindices, but
for more levels and more complex structures, it can becomes
harder to read. The blanks also keep their width, and the  result
isn't very attractive when presented as a single line:

``` python
labels = [('foo', '2012-07-26T00:00:00', 'b5c2700'),
    ...:  ('foo', '2012-08-06T00:00:00', '900b2ca'),
    ...:  ('foo', '2012-08-15T00:00:00', '07f1ce0'),
    ...:  ('foo', '2012-09-25T00:00:00', '5c93e83'),
    ...:  ('foo', '2012-09-25T00:00:00', '9345bba')]
    ...: pd.MultiIndex.from_tuples(labels)

MultiIndex
[append_frame_single_homogenous  2012-07-26  b5c2700,                     2012-08-06  00b2ca,                                 2012-08-15  07f1ce0,                                 2012-09-25  5c93e83,                                             9345bba]

```



Discussion:
Why not have it repr as if it were the columns of a DataFrame?


Resolving patch:
diff --git a/pandas/core/index.py b/pandas/core/index.py
--- a/pandas/core/index.py
+++ b/pandas/core/index.py
@@ -1,6 +1,6 @@
 # pylint: disable=E1101,E1103,W0232
 from functools import partial
-from pandas.compat import range, zip, lrange, lzip
+from pandas.compat import range, zip, lrange, lzip, u
 from pandas import compat
 import numpy as np
 
@@ -17,6 +17,10 @@
 from pandas.core.common import _values_from_object, is_float, is_integer
 from pandas.core.config import get_option
 
+# simplify
+default_pprint = lambda x: com.pprint_thing(x, escape_chars=('\t', '\r', '\n'),
+                                            quote_strings=True)
+
 
 __all__ = ['Index']
 
@@ -2052,18 +2056,40 @@ def _array_values(self):
     def dtype(self):
         return np.dtype('O')
 
+    def __repr__(self):
+        encoding = get_option('display.encoding')
+        attrs = [('levels', default_pprint(self.levels)),
+                 ('labels', default_pprint(self.labels))]
+        if not all(name is None for name in self.names):
+            attrs.append(('names', default_pprint(self.names)))
+        if self.sortorder is not None:
+            attrs.append(('sortorder', default_pprint(self.sortorder)))
+
+        space = ' ' * (len(self.__class__.__name__) + 1)
+        prepr = (u("\n%s") % space).join([u("%s=%s") % (k, v)
+                                          for k, v in attrs])
+        res = u("%s(%s)") % (self.__class__.__name__, prepr)
+
+        if not compat.PY3:
+            # needs to be str in Python 2
+            res = res.encode(encoding)
+        return res
+
+
     def __unicode__(self):
         """
         Return a string representation for a particular Index
 
         Invoked by unicode(df) in py2 only. Yields a Unicode String in both py2/py3.
         """
-        output = 'MultiIndex\n%s'
-
-        summary = com.pprint_thing(self, escape_chars=('\t', '\r', '\n'),
-                                   quote_strings=True)
-
-        return output % summary
+        rows = self.format(names=True)
+        max_rows = get_option('display.max_rows')
+        if len(rows) > max_rows:
+            spaces = (len(rows[0]) - 3) // 2
+            centered = ' ' * spaces
+            half = max_rows // 2
+            rows = rows[:half] + [centered + '...' + centered] + rows[-half:]
+        return "\n".join(rows)
 
     def __len__(self):
         return len(self.labels[0])


Test patch: