|
7 | 7 | import pandas.core.nanops as nanops |
8 | 8 | import pandas.lib as lib |
9 | 9 | from pandas.util.decorators import Appender, cache_readonly, deprecate_kwarg |
10 | | -from pandas.core.strings import StringMethods |
11 | 10 | from pandas.core.common import AbstractMethodError |
12 | 11 |
|
13 | 12 | _shared_docs = dict() |
@@ -111,6 +110,31 @@ def _reset_cache(self, key=None): |
111 | 110 | else: |
112 | 111 | self._cache.pop(key, None) |
113 | 112 |
|
| 113 | +class NoNewAttributesMixin(object): |
| 114 | + """Mixin which prevents adding new attributes. |
| 115 | +
|
| 116 | + Prevents additional attributes via xxx.attribute = "something" after a call to |
| 117 | + `self.__freeze()`. Mainly used to prevent the user from using wrong attrirbutes |
| 118 | + on a accessor (`Series.cat/.str/.dt`). |
| 119 | +
|
| 120 | + If you really want to add a new attribute at a later time, you need to use |
| 121 | + `object.__setattr__(self, key, value)`. |
| 122 | + """ |
| 123 | + |
| 124 | + def _freeze(self): |
| 125 | + """Prevents setting additional attributes""" |
| 126 | + object.__setattr__(self, "__frozen", True) |
| 127 | + |
| 128 | + |
| 129 | + # prevent adding any attribute via s.xxx.new_attribute = ... |
| 130 | + def __setattr__(self, key, value): |
| 131 | + # _cache is used by a decorator |
| 132 | + # dict lookup instead of getattr as getattr is false for getter which error |
| 133 | + if getattr(self, "__frozen", False) and not (key in type(self).__dict__ or key == "_cache"): |
| 134 | + raise AttributeError( "You cannot add any new attribute '{key}'".format(key=key)) |
| 135 | + object.__setattr__(self, key, value) |
| 136 | + |
| 137 | + |
114 | 138 | class PandasDelegate(PandasObject): |
115 | 139 | """ an abstract base class for delegating methods/properties """ |
116 | 140 |
|
@@ -547,41 +571,6 @@ def searchsorted(self, key, side='left'): |
547 | 571 | #### needs tests/doc-string |
548 | 572 | return self.values.searchsorted(key, side=side) |
549 | 573 |
|
550 | | - # string methods |
551 | | - def _make_str_accessor(self): |
552 | | - from pandas.core.series import Series |
553 | | - from pandas.core.index import Index |
554 | | - if isinstance(self, Series) and not com.is_object_dtype(self.dtype): |
555 | | - # this really should exclude all series with any non-string values, |
556 | | - # but that isn't practical for performance reasons until we have a |
557 | | - # str dtype (GH 9343) |
558 | | - raise AttributeError("Can only use .str accessor with string " |
559 | | - "values, which use np.object_ dtype in " |
560 | | - "pandas") |
561 | | - elif isinstance(self, Index): |
562 | | - # see scc/inferrence.pyx which can contain string values |
563 | | - allowed_types = ('string', 'unicode', 'mixed', 'mixed-integer') |
564 | | - if self.inferred_type not in allowed_types: |
565 | | - message = ("Can only use .str accessor with string values " |
566 | | - "(i.e. inferred_type is 'string', 'unicode' or 'mixed')") |
567 | | - raise AttributeError(message) |
568 | | - if self.nlevels > 1: |
569 | | - message = "Can only use .str accessor with Index, not MultiIndex" |
570 | | - raise AttributeError(message) |
571 | | - return StringMethods(self) |
572 | | - |
573 | | - str = AccessorProperty(StringMethods, _make_str_accessor) |
574 | | - |
575 | | - def _dir_additions(self): |
576 | | - return set() |
577 | | - |
578 | | - def _dir_deletions(self): |
579 | | - try: |
580 | | - getattr(self, 'str') |
581 | | - except AttributeError: |
582 | | - return set(['str']) |
583 | | - return set() |
584 | | - |
585 | 574 | _shared_docs['drop_duplicates'] = ( |
586 | 575 | """Return %(klass)s with duplicate values removed |
587 | 576 |
|
|
0 commit comments