Skip to content

REF: Changed ExtensionDtype inheritance #20363

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 78 additions & 66 deletions pandas/core/dtypes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,16 @@
from pandas.errors import AbstractMethodError


class ExtensionDtype(object):
"""A custom data type, to be paired with an ExtensionArray.

Notes
-----
The interface includes the following abstract methods that must
be implemented by subclasses:

* type
* name
* construct_from_string

This class does not inherit from 'abc.ABCMeta' for performance reasons.
Methods and properties required by the interface raise
``pandas.errors.AbstractMethodError`` and no ``register`` method is
provided for registering virtual subclasses.
"""

def __str__(self):
return self.name
class _DtypeOpsMixin(object):
# Not all of pandas' extension dtypes are compatibile with
# the new ExtensionArray interface. This means PandasExtensionDtype
# can't subclass ExtensionDtype yet, as is_extension_array_dtype would
# incorrectly say that these types are extension types.
#
# In the interim, we put methods that are shared between the two base
# classes ExtensionDtype and PandasExtensionDtype here. Both those base
# classes will inherit from this Mixin. Once everything is compatible, this
# class's methods can be moved to ExtensionDtype and removed.

def __eq__(self, other):
"""Check whether 'other' is equal to self.
Expand Down Expand Up @@ -52,6 +42,74 @@ def __eq__(self, other):
def __ne__(self, other):
return not self.__eq__(other)

@property
def names(self):
# type: () -> Optional[List[str]]
"""Ordered list of field names, or None if there are no fields.

This is for compatibility with NumPy arrays, and may be removed in the
future.
"""
return None

@classmethod
def is_dtype(cls, dtype):
"""Check if we match 'dtype'.

Parameters
----------
dtype : object
The object to check.

Returns
-------
is_dtype : bool

Notes
-----
The default implementation is True if

1. ``cls.construct_from_string(dtype)`` is an instance
of ``cls``.
2. ``dtype`` is an object and is an instance of ``cls``
3. ``dtype`` has a ``dtype`` attribute, and any of the above
conditions is true for ``dtype.dtype``.
"""
dtype = getattr(dtype, 'dtype', dtype)

if isinstance(dtype, np.dtype):
return False
elif dtype is None:
return False
elif isinstance(dtype, cls):
return True
try:
return cls.construct_from_string(dtype) is not None
except TypeError:
return False


class ExtensionDtype(_DtypeOpsMixin):
"""A custom data type, to be paired with an ExtensionArray.

Notes
-----
The interface includes the following abstract methods that must
be implemented by subclasses:

* type
* name
* construct_from_string

This class does not inherit from 'abc.ABCMeta' for performance reasons.
Methods and properties required by the interface raise
``pandas.errors.AbstractMethodError`` and no ``register`` method is
provided for registering virtual subclasses.
"""

def __str__(self):
return self.name

@property
def type(self):
# type: () -> type
Expand Down Expand Up @@ -87,16 +145,6 @@ def name(self):
"""
raise AbstractMethodError(self)

@property
def names(self):
# type: () -> Optional[List[str]]
"""Ordered list of field names, or None if there are no fields.

This is for compatibility with NumPy arrays, and may be removed in the
future.
"""
return None

@classmethod
def construct_from_string(cls, string):
"""Attempt to construct this type from a string.
Expand Down Expand Up @@ -128,39 +176,3 @@ def construct_from_string(cls, string):
... "'{}'".format(cls, string))
"""
raise AbstractMethodError(cls)

@classmethod
def is_dtype(cls, dtype):
"""Check if we match 'dtype'.

Parameters
----------
dtype : object
The object to check.

Returns
-------
is_dtype : bool

Notes
-----
The default implementation is True if

1. ``cls.construct_from_string(dtype)`` is an instance
of ``cls``.
2. ``dtype`` is an object and is an instance of ``cls``
3. ``dtype`` has a ``dtype`` attribute, and any of the above
conditions is true for ``dtype.dtype``.
"""
dtype = getattr(dtype, 'dtype', dtype)

if isinstance(dtype, np.dtype):
return False
elif dtype is None:
return False
elif isinstance(dtype, cls):
return True
try:
return cls.construct_from_string(dtype) is not None
except TypeError:
return False
6 changes: 3 additions & 3 deletions pandas/core/dtypes/dtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
from pandas import compat
from pandas.core.dtypes.generic import ABCIndexClass, ABCCategoricalIndex

from .base import ExtensionDtype
from .base import ExtensionDtype, _DtypeOpsMixin


class PandasExtensionDtype(ExtensionDtype):
class PandasExtensionDtype(_DtypeOpsMixin):
"""
A np.dtype duck-typed class, suitable for holding a custom dtype.

Expand Down Expand Up @@ -83,7 +83,7 @@ class CategoricalDtypeType(type):
pass


class CategoricalDtype(PandasExtensionDtype):
class CategoricalDtype(PandasExtensionDtype, ExtensionDtype):
"""
Type for categorical data with the categories and orderedness

Expand Down