是否有可能模仿python切片对象或子类?

时间:2016-11-23 15:49:11

标签: python arrays python-3.x slice subclassing

我想将一些元数据包含到python切片对象中,同时添加变量以指示切片中每个元素的索引。元数据用于标记切片正在检索的每个元素。我知道可以使用其他标记的数据结构,但在我的项目中,切片被预定义为numpy数组的一种下标,并在各个地方重复使用。所以,对我来说,找到一种方法来加入它是有道理的。

我在考虑对slice进行分类,但显然它不能是subclassed,这在链接问题的答案中已经清楚地解释了。从那时起有什么变化吗?

我想要做的是创建一个类似于以下的类:

class Subscript:
    def __init__(self, start, stop, step=None, labels=None):
        self.labels = labels
        self.slc = slice(start, stop, step)

        for i, l in zip(range(start, stop, step), labels):
            setattr(self, l, i)

并能够像这样使用它:

sub = Subscript(0, 5, labels=['s0', 's1', 's2', 's3', 's4'])

list(range(10))[sub]  # [0, 1, 2, 3, 4]

range(10)[sub.s0]  # 0

有没有办法在不必添加__call__方法返回切片的情况下执行此操作?不知怎的,我怀疑这是因为sub__getitem__的数组或列表不知道如何处理这个问题。我知道我可能只是将这些信息修补到slice,但我想知道这类事情是否可以在课堂上完成。

目前,我正在分别定义切片和切片元素,如:

sub = slice(0, 5)

s0, s1, s2, s3, s4 = range(5)

但是这种方法使得将多维数组的输出处理成dict变得更加困难,其中在多于1 sub的情况下键是下标元素组合,并且值是1d数组。

2 个答案:

答案 0 :(得分:1)

不,slice个对象仍然不能被分类。我在默认的Python(PySlice_Type)分支中为3.7定义了基于on the flags的内容:

Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,    /* tp_flags */

要允许对象充当基类,相应的Py_TPFLAGS_BASETYPEor在那里,因为它们具有允许的类型。以lists为例,他们的标志定义为:

Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
    Py_TPFLAGS_BASETYPE | Py_TPFLAGS_LIST_SUBCLASS,         /* tp_flags */

忽略其余部分,Py_TPFLAGS_BASETYPE在其中|允许其作为基类。

由于我无法在文档的某个地方找到这个提及的事实,我说它是一个实现细节,其理由我目前都不知道。我相信你可能绕过它的唯一方法就是放到C并在那里上课。

答案 1 :(得分:1)

我最终做的是子类numpy.ndarray因为我只是试图将切片传递给这种类型的对象(可以对列表执行相同的操作),然后重新实现__getitem__以便如果传入Subscript对象然后在传递给父方法之前首先提取切片。

看起来像:

import numpy as np

class SubArray(np.ndarray):
    def __new__(cls, input_array, subs=None):
        obj = np.asarray(input_array).view(cls)
        obj.subs = subs
        return obj

    def __getitem__(self, *args):
        args = tuple([a.slc if isinstance(a, SubRange) else a for a in args])
        return super().__getitem__(*args)

    def __array_finalize__(self, obj):
        if obj is None:
            return
        self.subs = getattr(obj, 'subs', None)


class Subscript:
    def __init__(self, labels, bounds=None):
        name, elements = labels

        if bounds:
            start, stop = bounds
        else:
            start, stop = 0, len(elements)

        self.size = stop - start
        self.slc = slice(start, stop)
        self.labels = labels
        self.name = name
        self.elements = elements

        for l, i in zip(labels, range(start, stop)):
            setattr(self, l, i)

可以这样使用:

sub = Subscript(('sub', ['s0', 's1', 's2', 's3', 's4']))

SubArray(np.arange(10), subs=sub)[sub]  # SubArray([0, 1, 2, 3, 4])

SubArray(np.arange(10), subs=sub)[sub.s0]  # 0

这更接近于我所避免的方法(即使用像xarray这样的方法),但结果仍然基本上是一个numpy数组,对我有用。