Reversing an arbitrary slice in Python
我正在寻找一个关于如何在Python中反转切片的通用方法。我读了这篇综合性的文章,其中有几个关于切片如何工作的很好的解释:了解python的切片表示法
然而,我无法找到一个关于如何计算反向切片的通用规则,反向切片以相反的顺序处理完全相同的元素。事实上,我很惊讶没有找到一个内置的方法来实现这一点。
我要找的是一种方法
1 2 3 4 5 6 7 | >>> import numpy as np >>> a = np.arange(30) >>> s = np.s_[10:20:2] >>> a[s] array([10, 12, 14, 16, 18]) >>> a[reversed_slice(s,len(a))] array([18, 16, 14, 12, 10]) |
我试过但不起作用的是:
1 2 3 4 5 6 7 8 9 10 11 12 | def reversed_slice(slice_, len_): """ Reverses a slice (selection in array of length len_), addressing the same elements in reverse order. """ assert isinstance(slice_, slice) instart, instop, instep = slice_.indices(len_) if instep > 0: start, stop, step = instop - 1, instart - 1, -instep else: start, stop, step = instop + 1, instart + 1, -instep return slice(start, stop, step) |
对于
1 2 3 4 5 6 7 | >>> import numpy as np >>> a = np.arange(30) >>> s = np.s_[10:20:2] >>> a[s] array([10, 12, 14, 16, 18]) >>> a[reversed_slice(s,len(a))] array([19, 17, 15, 13, 11]) |
所以我好像错过了一些像
笔记:
我知道还有其他的可能得到一个元素颠倒的序列,比如调用
reversed(a▼显示) 。这不是一个选项,因为我需要反转切片本身。原因是我在h5py 数据集上工作,这些数据集不允许片中存在负的step 值。一种简单但不是很优雅的方法是使用坐标列表,即
a[list(reversed(range(*s.indices(len(a)))))] 。由于h5py 要求清单中的指数必须按递增顺序给出,因此这也不是一种选择。
可以为
1 2 3 | >>> s = np.s_[20-2:10-2:-2] >>> a[s] array([18, 16, 14, 12, 10]) |
因此,您可以如下构建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | >>> def reversed_slice(s): ... """ ... Reverses a slice ... """ ... m = (s.stop-s.start) % s.step or s.step ... return slice(s.stop-m, s.start-m, -s.step) ... >>> a = np.arange(30) >>> s = np.s_[10:20:2] >>> a[reversed_slice(s)] array([18, 16, 14, 12, 10]) >>> >>> a[reversed_slice(reversed_slice(s))] array([10, 12, 14, 16, 18]) >>> |
我只是想用这个问题的答案,但是当测试发现仍然有一些情况会默默地给出错误的结果。-
以下从其他答案发展而来的逆向切片函数的定义似乎正确地涵盖了这些情况。-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | def reversed_slice(s, len_): """ Reverses a slice selection on a sequence of length len_, addressing the same elements in reverse order. """ assert isinstance(s, slice) instart, instop, instep = s.indices(len_) if (instop < instart and instep > 0) or (instop > instart and instep < 0) \ or (instop == 0 and instart == 0) : return slice(0,0,None) overstep = abs(instop-instart) % abs(instep) if overstep == 0 : overstep = abs(instep) if instep > 0: start = instop - overstep stop = instart - 1 else : start = instop + overstep stop = instart + 1 if stop < 0 : stop = None return slice(start, stop, -instep) |
我找到了一个基于Sunitha答案的有效解决方案(编辑:也实现了Warwick的答案):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | def reversed_slice(s, len_): """ Reverses a slice selection on a sequence of length len_, addressing the same elements in reverse order. """ assert isinstance(s, slice) instart, instop, instep = s.indices(len_) if (instop < instart and instep > 0) or (instop > instart and instep < 0) \ or (instop == 0 and instart == 0): return slice(0, 0, None) m = (instop - instart) % instep or instep if instep > 0 and instart - m < 0: outstop = None else: outstop = instart - m if instep < 0 and instop - m > len_: outstart = None else: outstart = instop - m return slice(outstart, outstop, -instep) |
它使用
此解决方案仅在提供要寻址的序列长度时有效。我觉得没有办法解决这个问题。如果有,或者有更简单的方法,我愿意接受建议!
到目前为止,我也没有找到任何内置的,但什么工作,即使是消极的步骤:
1 2 3 4 5 6 7 8 9 10 11 12 | def invert_slice(start, stop, step=1): distance = stop - start step_distance = distance // step expected_distance = step_distance * step if expected_distance != distance: expected_distance += step new_start = start + expected_distance - step new_stop = start - step return slice(new_start, new_stop, -step) |
这给了你
1 2 3 4 | >>> import numpy as np >>> a = np.arange(30) >>> s = np.s_[24:10:-1] >>> expected = list(reversed(a[s])) |
[18、16、14、12、10]
1 2 | >>> # resulting slice >>> result = invert_slice(s.start, s.stop, s.step) |
切片(18,8,-2)
1 2 | >>> assert np.allclose(expected, a[result]),"Invalid Slice %s" % result >>> a[result] |
[18 16 14 12 10]它们是相等的;-)
你在
1 2 3 4 5 6 7 8 9 10 11 12 | overstep = abs(instop-instart) % abs(instep) if overstep == 0 : overstep = abs(instep) if instep > 0: start = instop - overstep stop = instart - 1 else : start = instop + overstep stop = instart + 1 step = -instep |
一旦你把它放到你的代码中,一切都会正常工作。