【问题标题】:Is it possible to mimic a python slice object or subclass?是否可以模仿 python 切片对象或子类?
【发布时间】:2017-04-07 17:12:05
【问题描述】:

我想在 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,但我想知道这种事情是否可以在课堂上完成。

目前,我正在分别定义 slice 和 slice 元素,例如:

sub = slice(0, 5)

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

但是这种方法使得将多维数组的输出处理成字典变得更加困难,其中键是下标元素组合,在超过 1 个sub 和值是一维数组的情况下。

【问题讨论】:

  • 请不要使用exec动态设置属性,setattr更适合这个。另外,您的__init__ 没有self
  • 谢谢!忘了那个

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


【解决方案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 数组并且对我有用。

【讨论】:

    【解决方案2】:

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

    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,    /* tp_flags */
    

    为了允许对象充当基类,适当的Py_TPFLAGS_BASETYPE 将在其中添加or,因为它们定义了允许的类型。以lists为例,他们的flag定义为:

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

    忽略其余部分,Py_TPFLAGS_BASETYPE|'ed 在那里允许它充当基类。

    从我无法在文档中的某处找到这一点的事实来看,我会说这是一个实现细节,我目前不知道其基本原理。我相信您可能绕过它的唯一方法是转到C 并在那里上课。

    【讨论】:

    • 谢谢。看起来这比我想象的要棘手。因此,即使猴子修补 slice 似乎也不可行。我只是得到一个AttributeError
    • 是的@pbreach,如果我没记错的话,list.__getitem__(以及其他容器的行为应该类似)显式检查 slice 类型作为传递给它的参数或 int 值(至少, 提供 __index__ 方法的对象,该方法返回适当的 int)。您可能能做的最好的事情实际上是创建一个新的list 子类型,它允许自定义对象的行为类似于slices(可能通过组合?)。
    • 我基本上按照你的建议做了,除了子类化 numpy.ndarray 而不是 list 并将其发布在这里。为我工作!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-08
    • 2021-12-04
    • 2015-08-29
    • 2016-03-24
    相关资源
    最近更新 更多