Skip to content

Commit 411d13f

Browse files
committed
CLN: Refactor string methods and add PandasObject
Previous PandasObject becomes PandasContainer. New PandasObject becomes baseclass for more elements (like Index, Categorical, etc.), moves string methods to baseclass and subclassing objects need only define `__unicode__` methods to get all string methods for free (and Py2/3 compatible). CLN: Cleanup extraneous str methods from Panel CLN: Remove unnecessary string methods from frame CLN: Change name of TestPandasObjects --> TestPandasContainer
1 parent a16f243 commit 411d13f

File tree

7 files changed

+77
-110
lines changed

7 files changed

+77
-110
lines changed

pandas/core/base.py

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"""
2+
Base class(es) for all pandas objects.
3+
"""
4+
from pandas.util import py3compat
5+
6+
class StringMixin(object):
7+
"""implements string methods so long as object defines a `__unicode__` method.
8+
Handles Python2/3 compatibility transparently."""
9+
# side note - this could be made into a metaclass if more than one object nees
10+
def __str__(self):
11+
"""
12+
Return a string representation for a particular object.
13+
14+
Invoked by str(obj) in both py2/py3.
15+
Yields Bytestring in Py2, Unicode String in py3.
16+
"""
17+
18+
if py3compat.PY3:
19+
return self.__unicode__()
20+
return self.__bytes__()
21+
22+
def __bytes__(self):
23+
"""
24+
Return a string representation for a particular object.
25+
26+
Invoked by bytes(obj) in py3 only.
27+
Yields a bytestring in both py2/py3.
28+
"""
29+
from pandas.core.config import get_option
30+
31+
encoding = get_option("display.encoding")
32+
return self.__unicode__().encode(encoding, 'replace')
33+
34+
def __repr__(self):
35+
"""
36+
Return a string representation for a particular object.
37+
38+
Yields Bytestring in Py2, Unicode String in py3.
39+
"""
40+
return str(self)
41+
42+
class PandasObject(StringMixin):
43+
"""baseclass for various pandas objects"""
44+
45+
def __unicode__(self):
46+
"""
47+
Return a string representation for a particular object.
48+
49+
Invoked by unicode(obj) in py2 only. Yields a Unicode String in both
50+
py2/py3.
51+
"""
52+
# Should be overwritten by base classes
53+
return object.__repr__(self)

pandas/core/common.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@ def _isnull_new(obj):
6464
if lib.isscalar(obj):
6565
return lib.checknull(obj)
6666

67-
from pandas.core.generic import PandasObject
67+
from pandas.core.generic import PandasContainer
6868
if isinstance(obj, np.ndarray):
6969
return _isnull_ndarraylike(obj)
70-
elif isinstance(obj, PandasObject):
70+
elif isinstance(obj, PandasContainer):
7171
# TODO: optimize for DataFrame, etc.
7272
return obj.apply(isnull)
7373
elif isinstance(obj, list) or hasattr(obj, '__array__'):
@@ -91,10 +91,10 @@ def _isnull_old(obj):
9191
if lib.isscalar(obj):
9292
return lib.checknull_old(obj)
9393

94-
from pandas.core.generic import PandasObject
94+
from pandas.core.generic import PandasContainer
9595
if isinstance(obj, np.ndarray):
9696
return _isnull_ndarraylike_old(obj)
97-
elif isinstance(obj, PandasObject):
97+
elif isinstance(obj, PandasContainer):
9898
# TODO: optimize for DataFrame, etc.
9999
return obj.apply(_isnull_old)
100100
elif isinstance(obj, list) or hasattr(obj, '__array__'):

pandas/core/frame.py

-30
Original file line numberDiff line numberDiff line change
@@ -653,28 +653,6 @@ def _repr_fits_horizontal_(self,ignore_width=False):
653653

654654
return repr_width < width
655655

656-
def __str__(self):
657-
"""
658-
Return a string representation for a particular DataFrame
659-
660-
Invoked by str(df) in both py2/py3.
661-
Yields Bytestring in Py2, Unicode String in py3.
662-
"""
663-
664-
if py3compat.PY3:
665-
return self.__unicode__()
666-
return self.__bytes__()
667-
668-
def __bytes__(self):
669-
"""
670-
Return a string representation for a particular DataFrame
671-
672-
Invoked by bytes(df) in py3 only.
673-
Yields a bytestring in both py2/py3.
674-
"""
675-
encoding = com.get_option("display.encoding")
676-
return self.__unicode__().encode(encoding, 'replace')
677-
678656
def __unicode__(self):
679657
"""
680658
Return a string representation for a particular DataFrame
@@ -714,14 +692,6 @@ def __unicode__(self):
714692

715693
return value
716694

717-
def __repr__(self):
718-
"""
719-
Return a string representation for a particular DataFrame
720-
721-
Yields Bytestring in Py2, Unicode String in py3.
722-
"""
723-
return str(self)
724-
725695
def _repr_html_(self):
726696
"""
727697
Return a html representation for a particular DataFrame.

pandas/core/generic.py

+12-7
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
# pylint: disable=W0231,E1101
22

33
import numpy as np
4+
import pandas.lib as lib
5+
from pandas.core.base import PandasObject
46

57
from pandas.core.index import MultiIndex
68
import pandas.core.indexing as indexing
79
from pandas.core.indexing import _maybe_convert_indices
810
from pandas.tseries.index import DatetimeIndex
911
import pandas.core.common as com
10-
import pandas.lib as lib
1112

1213

1314
class PandasError(Exception):
1415
pass
1516

1617

17-
class PandasObject(object):
18+
class PandasContainer(PandasObject):
1819

1920
_AXIS_NUMBERS = {
2021
'index': 0,
@@ -52,6 +53,12 @@ def __hash__(self):
5253
raise TypeError('{0!r} objects are mutable, thus they cannot be'
5354
' hashed'.format(self.__class__.__name__))
5455

56+
def __unicode__(self):
57+
# unicode representation based upon iterating over self
58+
# (since, by definition, `PandasContainers` are iterable)
59+
prepr = '[%s]' % ','.join(map(com.pprint_thing, self))
60+
return '%s(%s)' % (self.__class__.__name__, prepr)
61+
5562

5663
#----------------------------------------------------------------------
5764
# Axis name business
@@ -578,9 +585,10 @@ def to_json(self, path_or_buf=None, orient=None, date_format='epoch',
578585

579586
# install the indexerse
580587
for _name, _indexer in indexing.get_indexers_list():
581-
PandasObject._create_indexer(_name,_indexer)
588+
PandasContainer._create_indexer(_name,_indexer)
582589

583-
class NDFrame(PandasObject):
590+
591+
class NDFrame(PandasContainer):
584592
"""
585593
N-dimensional analogue of DataFrame. Store multi-dimensional in a
586594
size-mutable, labeled data structure
@@ -633,9 +641,6 @@ def _constructor(self):
633641
def axes(self):
634642
return self._data.axes
635643

636-
def __repr__(self):
637-
return 'NDFrame'
638-
639644
@property
640645
def values(self):
641646
return self._data.as_matrix()

pandas/core/panel.py

-30
Original file line numberDiff line numberDiff line change
@@ -466,28 +466,6 @@ def __invert__(self):
466466
#----------------------------------------------------------------------
467467
# Magic methods
468468

469-
def __str__(self):
470-
"""
471-
Return a string representation for a particular Panel
472-
473-
Invoked by str(df) in both py2/py3.
474-
Yields Bytestring in Py2, Unicode String in py3.
475-
"""
476-
477-
if py3compat.PY3:
478-
return self.__unicode__()
479-
return self.__bytes__()
480-
481-
def __bytes__(self):
482-
"""
483-
Return a string representation for a particular Panel
484-
485-
Invoked by bytes(df) in py3 only.
486-
Yields a bytestring in both py2/py3.
487-
"""
488-
encoding = com.get_option("display.encoding")
489-
return self.__unicode__().encode(encoding, 'replace')
490-
491469
def __unicode__(self):
492470
"""
493471
Return a string representation for a particular Panel
@@ -515,14 +493,6 @@ def axis_pretty(a):
515493
[class_name, dims] + [axis_pretty(a) for a in self._AXIS_ORDERS])
516494
return output
517495

518-
def __repr__(self):
519-
"""
520-
Return a string representation for a particular Panel
521-
522-
Yields Bytestring in Py2, Unicode String in py3.
523-
"""
524-
return str(self)
525-
526496
def __iter__(self):
527497
return iter(getattr(self, self._info_axis))
528498

pandas/core/series.py

+1-32
Original file line numberDiff line numberDiff line change
@@ -394,8 +394,7 @@ def f(self, axis=0, dtype=None, out=None, skipna=True, level=None):
394394
#----------------------------------------------------------------------
395395
# Series class
396396

397-
398-
class Series(pa.Array, generic.PandasObject):
397+
class Series(generic.PandasContainer, pa.Array):
399398
"""
400399
One-dimensional ndarray with axis labels (including time series).
401400
Labels need not be unique but must be any hashable type. The object
@@ -1096,28 +1095,6 @@ def reset_index(self, level=None, drop=False, name=None, inplace=False):
10961095

10971096
return df.reset_index(level=level, drop=drop)
10981097

1099-
def __str__(self):
1100-
"""
1101-
Return a string representation for a particular DataFrame
1102-
1103-
Invoked by str(df) in both py2/py3.
1104-
Yields Bytestring in Py2, Unicode String in py3.
1105-
"""
1106-
1107-
if py3compat.PY3:
1108-
return self.__unicode__()
1109-
return self.__bytes__()
1110-
1111-
def __bytes__(self):
1112-
"""
1113-
Return a string representation for a particular DataFrame
1114-
1115-
Invoked by bytes(df) in py3 only.
1116-
Yields a bytestring in both py2/py3.
1117-
"""
1118-
encoding = com.get_option("display.encoding")
1119-
return self.__unicode__().encode(encoding, 'replace')
1120-
11211098
def __unicode__(self):
11221099
"""
11231100
Return a string representation for a particular DataFrame
@@ -1142,14 +1119,6 @@ def __unicode__(self):
11421119
raise AssertionError()
11431120
return result
11441121

1145-
def __repr__(self):
1146-
"""
1147-
Return a string representation for a particular Series
1148-
1149-
Yields Bytestring in Py2, Unicode String in py3.
1150-
"""
1151-
return str(self)
1152-
11531122
def _tidy_repr(self, max_vals=20):
11541123
"""
11551124

pandas/io/tests/test_json/test_pandas.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333

3434
_mixed_frame = _frame.copy()
3535

36-
class TestPandasObjects(unittest.TestCase):
36+
class TestPandasContainer(unittest.TestCase):
3737

3838
def setUp(self):
3939
self.ts = tm.makeTimeSeries()
@@ -68,7 +68,7 @@ def _check_orient(df, orient, dtype=None, numpy=False, convert_axes=True, check_
6868
if type(detail) == raise_ok:
6969
return
7070
raise
71-
71+
7272
unser = unser.sort()
7373

7474
if dtype is False:
@@ -104,7 +104,7 @@ def _check_all_orients(df, dtype=None, convert_axes=True, raise_ok=None):
104104
_check_orient(df, "split", dtype=dtype)
105105
_check_orient(df, "index", dtype=dtype)
106106
_check_orient(df, "values", dtype=dtype)
107-
107+
108108
_check_orient(df, "columns", dtype=dtype, convert_axes=False)
109109
_check_orient(df, "records", dtype=dtype, convert_axes=False)
110110
_check_orient(df, "split", dtype=dtype, convert_axes=False)
@@ -347,7 +347,7 @@ def test_convert_dates(self):
347347
assert_series_equal(result,ts)
348348

349349
def test_date_format(self):
350-
350+
351351
df = self.tsframe.copy()
352352
df['date'] = Timestamp('20130101')
353353
df_orig = df.copy()
@@ -412,7 +412,7 @@ def test_misc_example(self):
412412
@network
413413
@slow
414414
def test_round_trip_exception_(self):
415-
# GH 3867
415+
# GH 3867
416416

417417
df = pd.read_csv('https://raw.github.com/hayd/lahman2012/master/csvs/Teams.csv')
418418
s = df.to_json()
@@ -429,9 +429,9 @@ def test_url(self):
429429
result = read_json(url,convert_dates=True)
430430
for c in ['created_at','closed_at','updated_at']:
431431
self.assert_(result[c].dtype == 'datetime64[ns]')
432-
432+
433433
url = 'http://search.twitter.com/search.json?q=pandas%20python'
434434
result = read_json(url)
435-
435+
436436
except urllib2.URLError:
437437
raise nose.SkipTest

0 commit comments

Comments
 (0)