Skip to content

Metaclass approach #3

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

Closed
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
1 change: 0 additions & 1 deletion pandas/core/arrays/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from ._reshaping import implement_2d # noqa:F401
from .base import ( # noqa: F401
ExtensionArray,
ExtensionOpsMixin,
Expand Down
122 changes: 0 additions & 122 deletions pandas/core/arrays/_reshaping.py
Original file line number Diff line number Diff line change
@@ -1,135 +1,13 @@
"""
Utilities for implementing 2D compatibility for 1D ExtensionArrays.
"""
from functools import wraps
from typing import Tuple

import numpy as np

from pandas._libs.lib import is_integer


def implement_2d(cls):
"""
A decorator to take a 1-dimension-only ExtensionArray subclass and make
it support limited 2-dimensional operations.
"""
from pandas.core.arrays import ExtensionArray

# For backwards-compatibility, if an EA author implemented __len__
# but not size, we use that __len__ method to get an array's size.
has_size = cls.size is not ExtensionArray.size
has_shape = cls.shape is not ExtensionArray.shape
has_len = cls.__len__ is not ExtensionArray.__len__

if not has_size and has_len:
cls.size = property(cls.__len__)
cls.__len__ = ExtensionArray.__len__

elif not has_size and has_shape:

@property
def size(self) -> int:
return np.prod(self.shape)

cls.size = size

orig_copy = cls.copy

@wraps(orig_copy)
def copy(self):
result = orig_copy(self)
result._shape = self._shape
return result

cls.copy = copy

orig_getitem = cls.__getitem__

def __getitem__(self, key):
if self.ndim == 1:
return orig_getitem(self, key)

key = expand_key(key, self.shape)
if is_integer(key[0]):
assert key[0] in [0, -1]
result = orig_getitem(self, key[1])
return result

if isinstance(key[0], slice):
if slice_contains_zero(key[0]):
result = orig_getitem(self, key[1])
result._shape = (1, result.size)
return result

raise NotImplementedError(key)
# TODO: ellipses?
raise NotImplementedError(key)

cls.__getitem__ = __getitem__

orig_take = cls.take

# kwargs for compat with Interval
# allow_fill=None instead of False is for compat with Categorical
def take(self, indices, allow_fill=None, fill_value=None, axis=0, **kwargs):
if self.ndim == 1 and axis == 0:
return orig_take(
self, indices, allow_fill=allow_fill, fill_value=fill_value, **kwargs
)

if self.ndim != 2 or self.shape[0] != 1:
raise NotImplementedError
if axis not in [0, 1]:
raise ValueError(axis)
if kwargs:
raise ValueError(
"kwargs should not be passed in the 2D case, "
"are only included for compat with Interval"
)

if axis == 1:
result = orig_take(
self, indices, allow_fill=allow_fill, fill_value=fill_value
)
result._shape = (1, result.size)
return result

# For axis == 0, because we only support shape (1, N)
# there are only limited indices we can accept
if len(indices) != 1:
# TODO: we could probably support zero-len here
raise NotImplementedError

def take_item(n):
if n == -1:
seq = [fill_value] * self.shape[1]
return type(self)._from_sequence(seq)
else:
return self[n, :]

arrs = [take_item(n) for n in indices]
result = type(self)._concat_same_type(arrs)
result.shape = (len(indices), self.shape[1])
return result

cls.take = take

orig_iter = cls.__iter__

def __iter__(self):
if self.ndim == 1:
for obj in orig_iter(self):
yield obj
else:
for n in range(self.shape[0]):
yield self[n]

cls.__iter__ = __iter__

return cls


def slice_contains_zero(slc: slice) -> bool:
if slc == slice(None):
return True
Expand Down
Loading