在__getitem__中实现切片

143 投票
5 回答
105973 浏览
提问于 2025-04-15 23:20

我正在尝试为我创建的一个类实现切片功能,这个类是用来生成向量表示的。

到目前为止,我有了这段代码,我认为它可以正确实现切片功能。但是每当我像这样使用 v[4],其中 v 是一个向量时,Python 就会报错,提示参数不够。所以我正在想办法如何在我的类中定义 __getitem__ 这个特殊方法,以便同时处理普通索引和切片。

def __getitem__(self, start, stop, step):
    index = start
    if stop == None:
        end = start + 1
    else:
        end = stop
    if step == None:
        stride = 1
    else:
        stride = step
    return self.__data[index:end:stride]

5 个回答

35

如何定义getitem类来处理普通索引和切片?

当你在下标表示法中使用冒号时,会自动创建切片对象——这就是传递给__getitem__的内容。你可以用isinstance来检查你是否得到了一个切片对象:

from __future__ import print_function

class Sliceable(object):
    def __getitem__(self, subscript):
        if isinstance(subscript, slice):
            # do your handling for a slice object:
            print(subscript.start, subscript.stop, subscript.step)
        else:
            # Do your handling for a plain index
            print(subscript)

假设我们在使用一个范围对象,但我们希望切片返回列表,而不是新的范围对象(默认情况下是这样):

>>> range(1,100, 4)[::-1]
range(97, -3, -4)

我们不能直接继承范围对象,因为有一些内部限制,但我们可以让它来帮忙:

class Range:
    """like builtin range, but when sliced gives a list"""
    __slots__ = "_range"
    def __init__(self, *args):
        self._range = range(*args) # takes no keyword arguments.
    def __getattr__(self, name):
        return getattr(self._range, name)
    def __getitem__(self, subscript):
        result = self._range.__getitem__(subscript)
        if isinstance(subscript, slice):
            return list(result)
        else:
            return result

r = Range(100)

虽然我们没有一个完全可以替代的范围对象,但这个差不多可以:

>>> r[1:3]
[1, 2]
>>> r[1]
1
>>> 2 in r
True
>>> r.count(3)
1

为了更好地理解切片表示法,这里有一个Sliceable的使用示例:

>>> sliceme = Sliceable()
>>> sliceme[1]
1
>>> sliceme[2]
2
>>> sliceme[:]
None None None
>>> sliceme[1:]
1 None None
>>> sliceme[1:2]
1 2 None
>>> sliceme[1:2:3]
1 2 3
>>> sliceme[:2:3]
None 2 3
>>> sliceme[::3]
None None 3
>>> sliceme[::]
None None None
>>> sliceme[:]
None None None

注意:Python 2

在Python 2中,有一个已经不推荐使用的方法,当你继承某些内置类型时,可能需要重写它。

来自数据模型文档

object.__getslice__(self, i, j)

自2.0版本以来不推荐使用:支持切片对象作为__getitem__()方法的参数。(不过,CPython中的内置类型目前仍然实现__getslice__()。因此,在实现切片时,你必须在派生类中重写它。)

在Python 3中,这个方法已经被移除了。

89

我有一个“合成”的列表(也就是说,这个列表的数据量大到你不想把它全部放在内存里),我的 __getitem__ 方法是这样的:

def __getitem__(self, key):
    if isinstance(key, slice):
        # Get the start, stop, and step from the slice
        return [self[ii] for ii in xrange(*key.indices(len(self)))]
    elif isinstance(key, int):
        if key < 0: # Handle negative indices
            key += len(self)
        if key < 0 or key >= len(self):
            raise IndexError, "The index (%d) is out of range." % key
        return self.getData(key) # Get the data from elsewhere
    else:
        raise TypeError, "Invalid argument type."

不过这个切片返回的类型和原来的不一样,这本来是不应该的,但对我来说这样也能用。

153

当你对一个对象进行切片操作时,__getitem__()这个方法会接收到一个叫做slice的对象。你只需要查看这个slice对象里的startstopstep这几个部分,就能获取到切片的各个组成部分。

>>> class C(object):
...   def __getitem__(self, val):
...     print val
... 
>>> c = C()
>>> c[3]
3
>>> c[3:4]
slice(3, 4, None)
>>> c[3:4:-2]
slice(3, 4, -2)
>>> c[():1j:'a']
slice((), 1j, 'a')

撰写回答