反转 Python 中的任意切片
Reversing an arbitrary slice in Python
我正在寻找一种关于如何在 Python 中反转切片的通用方法。
我阅读了这篇综合文章 post,其中对切片的工作原理有几个很好的解释:
Understanding Python's slice notation
但是我无法找出关于如何计算反向切片的通用规则,该反向切片以相反的顺序处理完全相同的元素。我真的很惊讶没有找到这样做的内置方法。
我正在寻找的是一种方法 reversed_slice
,它可以像这样使用任意 start
、stop
和 step
值,包括负值:
>>> 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])
我试过但不起作用的是:
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
的步骤以及当最后一个寻址元素与 stop-1
一致时效果很好。对于其他情况,它不会:
>>> 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])
所以我似乎遗漏了一些关系,例如 (stop - start) % step
。
非常感谢任何有关如何编写通用方法的帮助。
备注:
我知道还有其他可能性可以得到一个具有相同元素的序列,比如调用 reversed(a[s])
。这不是一个选项,因为我需要反转切片本身。原因是我在 h5py
数据集上工作,这些数据集不允许切片中的负 step
值。
一种简单但不是很优雅的方法是使用坐标列表,即 a[list(reversed(range(*s.indices(len(a)))))]
。这也不是一个选项,因为 h5py
要求列表中的索引必须按递增顺序给出。
您可以为 step
指定负值。
>>> s = np.s_[20-2:10-2:-2]
>>> a[s]
array([18, 16, 14, 12, 10])
因此您可以按如下方式构建 reversed_slice
函数
>>> 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])
>>>
你在 start/stop
数学上犯了一些错误:
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
将其放入代码后,一切都会正常运行。
到目前为止,我也没有找到任何 内置函数 ,但即使是负步骤也能起作用:
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)
这给你
>>> import numpy as np
>>> a = np.arange(30)
>>> s = np.s_[24:10:-1]
>>> expected = list(reversed(a[s]))
[18, 16, 14, 12, 10]
>>> # resulting slice
>>> result = invert_slice(s.start, s.stop, s.step)
切片(18, 8, -2)
>>> assert np.allclose(expected, a[result]), "Invalid Slice %s" % result
>>> a[result]
[18 16 14 12 10]
他们是平等的 ;-)
我根据 Sunitha 的回答找到了一个可行的解决方案(编辑:也实现了 Warwick 的回答):
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)
它使用 slice.indices(len)
方法来扩展功能,因此它也可以与切片中的 None
条目一起使用,例如[::-1]
。 if
子句避免了边界问题。
此解决方案仅在提供要寻址的序列长度时才有效。我认为没有办法解决这个问题。如果有,或者如果有更简单的方法,我愿意接受建议!
我只是想用这个问题的答案,但在测试时发现仍然有一些情况会默默给出错误的结果-
从其他答案发展而来的 reversed_slice 函数的以下定义似乎正确地涵盖了这些情况 -
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)
我正在寻找一种关于如何在 Python 中反转切片的通用方法。 我阅读了这篇综合文章 post,其中对切片的工作原理有几个很好的解释: Understanding Python's slice notation
但是我无法找出关于如何计算反向切片的通用规则,该反向切片以相反的顺序处理完全相同的元素。我真的很惊讶没有找到这样做的内置方法。
我正在寻找的是一种方法 reversed_slice
,它可以像这样使用任意 start
、stop
和 step
值,包括负值:
>>> 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])
我试过但不起作用的是:
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
的步骤以及当最后一个寻址元素与 stop-1
一致时效果很好。对于其他情况,它不会:
>>> 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])
所以我似乎遗漏了一些关系,例如 (stop - start) % step
。
非常感谢任何有关如何编写通用方法的帮助。
备注:
我知道还有其他可能性可以得到一个具有相同元素的序列,比如调用
reversed(a[s])
。这不是一个选项,因为我需要反转切片本身。原因是我在h5py
数据集上工作,这些数据集不允许切片中的负step
值。一种简单但不是很优雅的方法是使用坐标列表,即
a[list(reversed(range(*s.indices(len(a)))))]
。这也不是一个选项,因为h5py
要求列表中的索引必须按递增顺序给出。
您可以为 step
指定负值。
>>> s = np.s_[20-2:10-2:-2]
>>> a[s]
array([18, 16, 14, 12, 10])
因此您可以按如下方式构建 reversed_slice
函数
>>> 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])
>>>
你在 start/stop
数学上犯了一些错误:
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
将其放入代码后,一切都会正常运行。
到目前为止,我也没有找到任何 内置函数 ,但即使是负步骤也能起作用:
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)
这给你
>>> import numpy as np
>>> a = np.arange(30)
>>> s = np.s_[24:10:-1]
>>> expected = list(reversed(a[s]))
[18, 16, 14, 12, 10]
>>> # resulting slice
>>> result = invert_slice(s.start, s.stop, s.step)
切片(18, 8, -2)
>>> assert np.allclose(expected, a[result]), "Invalid Slice %s" % result
>>> a[result]
[18 16 14 12 10] 他们是平等的 ;-)
我根据 Sunitha 的回答找到了一个可行的解决方案(编辑:也实现了 Warwick 的回答):
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)
它使用 slice.indices(len)
方法来扩展功能,因此它也可以与切片中的 None
条目一起使用,例如[::-1]
。 if
子句避免了边界问题。
此解决方案仅在提供要寻址的序列长度时才有效。我认为没有办法解决这个问题。如果有,或者如果有更简单的方法,我愿意接受建议!
我只是想用这个问题的答案,但在测试时发现仍然有一些情况会默默给出错误的结果-
从其他答案发展而来的 reversed_slice 函数的以下定义似乎正确地涵盖了这些情况 -
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)