在__ getitem__ 中实现切片

我试图为一个创建向量表示的类实现切片功能。

到目前为止,我已经有了这段代码,我相信它将正确地实现这个片,但是每当我执行类似于 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]
90976 次浏览

当对象被切片时,__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')

正确的方法是让 __getitem__接受一个参数,这个参数可以是一个数字,也可以是一个 切片对象。

我有一个“合成”列表(其中的数据大于你想在内存中创建的数据) ,我的 __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."

切片不返回相同的类型,这是一个禁忌,但它为我工作。

如何定义 gettem 类来处理纯索引和切片?

在下标符号中使用冒号时,会自动创建 Slice 对象——传递给 __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 对象,但是我们希望片返回列表,而不是新的 range 对象(正如它所做的那样) :

>>> 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)

我们没有一个完全可替换的 Range 对象,但它非常接近:

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

为了更好地理解片表示法,下面是 Slicable 的示例用法:

>>> 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,请注意:

在 Python2中,有一个不推荐的方法,在子类化某些内置类型时可能需要重写这个方法。

来自 数据模型文档:

object.__getslice__(self, i, j)

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

这在 Python3中已经不存在了。

为了扩展 Aaron 的答案,对于像 numpy这样的东西,你可以通过检查 given是否是 tuple来进行多维切片:

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


sliceme = Sliceable()
sliceme[1]
sliceme[::]
sliceme[1:, ::2]

```

产出:

('plain', 1)
('slice', None, None, None)
('multidim', (slice(1, None, None), slice(None, None, 2)))