diff --git a/pandas/core/accessor.py b/pandas/core/accessor.py
new file mode 100644
index 0000000000000..9f8556d1e6961
--- /dev/null
+++ b/pandas/core/accessor.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+"""
+
+accessor.py contains base classes for implementing accessor properties
+that can be mixed into or pinned onto other pandas classes.
+
+"""
+
+
+class DirNamesMixin(object):
+    _accessors = frozenset([])
+
+    def _dir_deletions(self):
+        """ delete unwanted __dir__ for this object """
+        return self._accessors
+
+    def _dir_additions(self):
+        """ add addtional __dir__ for this object """
+        rv = set()
+        for accessor in self._accessors:
+            try:
+                getattr(self, accessor)
+                rv.add(accessor)
+            except AttributeError:
+                pass
+        return rv
+
+    def __dir__(self):
+        """
+        Provide method name lookup and completion
+        Only provide 'public' methods
+        """
+        rv = set(dir(type(self)))
+        rv = (rv - self._dir_deletions()) | self._dir_additions()
+        return sorted(rv)
diff --git a/pandas/core/base.py b/pandas/core/base.py
index 8f21e3125a27e..b15431464b166 100644
--- a/pandas/core/base.py
+++ b/pandas/core/base.py
@@ -18,6 +18,7 @@
 from pandas.util._decorators import (Appender, cache_readonly,
                                      deprecate_kwarg, Substitution)
 from pandas.core.common import AbstractMethodError
+from pandas.core.accessor import DirNamesMixin
 
 _shared_docs = dict()
 _indexops_doc_kwargs = dict(klass='IndexOpsMixin', inplace='',
@@ -72,7 +73,7 @@ def __repr__(self):
         return str(self)
 
 
-class PandasObject(StringMixin):
+class PandasObject(StringMixin, DirNamesMixin):
 
     """baseclass for various pandas objects"""
 
@@ -91,23 +92,6 @@ def __unicode__(self):
         # Should be overwritten by base classes
         return object.__repr__(self)
 
-    def _dir_additions(self):
-        """ add addtional __dir__ for this object """
-        return set()
-
-    def _dir_deletions(self):
-        """ delete unwanted __dir__ for this object """
-        return set()
-
-    def __dir__(self):
-        """
-        Provide method name lookup and completion
-        Only provide 'public' methods
-        """
-        rv = set(dir(type(self)))
-        rv = (rv - self._dir_deletions()) | self._dir_additions()
-        return sorted(rv)
-
     def _reset_cache(self, key=None):
         """
         Reset cached properties. If ``key`` is passed, only clears that key.
@@ -140,7 +124,7 @@ class NoNewAttributesMixin(object):
 
     Prevents additional attributes via xxx.attribute = "something" after a
     call to `self.__freeze()`. Mainly used to prevent the user from using
-    wrong attrirbutes on a accessor (`Series.cat/.str/.dt`).
+    wrong attributes on a accessor (`Series.cat/.str/.dt`).
 
     If you really want to add a new attribute at a later time, you need to use
     `object.__setattr__(self, key, value)`.
diff --git a/pandas/core/generic.py b/pandas/core/generic.py
index 2d52eed81d22b..04debef2fcac0 100644
--- a/pandas/core/generic.py
+++ b/pandas/core/generic.py
@@ -192,8 +192,9 @@ def __unicode__(self):
 
     def _dir_additions(self):
         """ add the string-like attributes from the info_axis """
-        return set([c for c in self._info_axis
-                    if isinstance(c, string_types) and isidentifier(c)])
+        additions = set([c for c in self._info_axis
+                         if isinstance(c, string_types) and isidentifier(c)])
+        return super(NDFrame, self)._dir_additions().union(additions)
 
     @property
     def _constructor_sliced(self):
diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py
index 4aecc75d95971..d614b69c85afa 100644
--- a/pandas/core/indexes/base.py
+++ b/pandas/core/indexes/base.py
@@ -52,7 +52,7 @@
 import pandas.core.sorting as sorting
 from pandas.io.formats.printing import pprint_thing
 from pandas.core.ops import _comp_method_OBJECT_ARRAY
-from pandas.core.strings import StringAccessorMixin
+from pandas.core import strings
 from pandas.core.config import get_option
 
 
@@ -98,7 +98,7 @@ def _new_Index(cls, d):
     return cls.__new__(cls, **d)
 
 
-class Index(IndexOpsMixin, StringAccessorMixin, PandasObject):
+class Index(IndexOpsMixin, PandasObject):
     """
     Immutable ndarray implementing an ordered, sliceable set. The basic object
     storing axis labels for all pandas objects
@@ -151,6 +151,11 @@ class Index(IndexOpsMixin, StringAccessorMixin, PandasObject):
 
     _engine_type = libindex.ObjectEngine
 
+    _accessors = frozenset(['str'])
+
+    # String Methods
+    str = base.AccessorProperty(strings.StringMethods)
+
     def __new__(cls, data=None, dtype=None, copy=False, name=None,
                 fastpath=False, tupleize_cols=True, **kwargs):
 
diff --git a/pandas/core/series.py b/pandas/core/series.py
index c8282450b77a9..06504a4059a2f 100644
--- a/pandas/core/series.py
+++ b/pandas/core/series.py
@@ -114,8 +114,7 @@ def wrapper(self):
 # Series class
 
 
-class Series(base.IndexOpsMixin, strings.StringAccessorMixin,
-             generic.NDFrame,):
+class Series(base.IndexOpsMixin, generic.NDFrame):
     """
     One-dimensional ndarray with axis labels (including time series).
 
@@ -2924,18 +2923,8 @@ def to_period(self, freq=None, copy=True):
     # Categorical methods
     cat = base.AccessorProperty(CategoricalAccessor)
 
-    def _dir_deletions(self):
-        return self._accessors
-
-    def _dir_additions(self):
-        rv = set()
-        for accessor in self._accessors:
-            try:
-                getattr(self, accessor)
-                rv.add(accessor)
-            except AttributeError:
-                pass
-        return rv
+    # String Methods
+    str = base.AccessorProperty(strings.StringMethods)
 
     # ----------------------------------------------------------------------
     # Add plotting methods to Series
diff --git a/pandas/core/strings.py b/pandas/core/strings.py
index 0b1db0277eee3..2f95e510bba5e 100644
--- a/pandas/core/strings.py
+++ b/pandas/core/strings.py
@@ -16,7 +16,7 @@
 
 from pandas.core.algorithms import take_1d
 import pandas.compat as compat
-from pandas.core.base import AccessorProperty, NoNewAttributesMixin
+from pandas.core.base import NoNewAttributesMixin
 from pandas.util._decorators import Appender
 import re
 import pandas._libs.lib as lib
@@ -1920,20 +1920,4 @@ def _make_accessor(cls, data):
                 message = ("Can only use .str accessor with Index, not "
                            "MultiIndex")
                 raise AttributeError(message)
-        return StringMethods(data)
-
-
-class StringAccessorMixin(object):
-    """ Mixin to add a `.str` acessor to the class."""
-
-    str = AccessorProperty(StringMethods)
-
-    def _dir_additions(self):
-        return set()
-
-    def _dir_deletions(self):
-        try:
-            getattr(self, 'str')
-        except AttributeError:
-            return set(['str'])
-        return set()
+        return cls(data)