关于字符串:python反向跨步切片

Python reverse-stride slicing

我的问题的一个具体例子是,"在这个例子中,我怎样才能得到‘3210’。"

1
2
3
4
5
6
7
8
9
10
11
>>> foo = '0123456'
>>> foo[0:4]
'0123'
>>> foo[::-1]
'6543210'
>>> foo[4:0:-1] # I was shooting for '3210' but made a fencepost error, that's fine, but...
'4321'
>>> foo[3:-1:-1] # How can I get '3210'?
''
>>> foo[3:0:-1]
'321'

我能写foo[4:0:-1]、foo[5:1:-1]等,并得到我所期望的,这似乎很奇怪,但没有办法写这个切片,所以我得到了"3210"。

这样做的一种临时方法是foo[0:4][::-1],但这会在进程中创建两个字符串对象。我将执行这个操作几十亿次,所以每个字符串操作都是昂贵的。

我一定错过了一些愚蠢和容易的事情。谢谢你的帮助!


只需排除结束范围索引…

1
2
>>> foo[3::-1]
'3210'

讽刺的是,我认为你没有尝试过的唯一选择。


如果您在寻找比扩展切片符号更易于阅读的东西:

1
2
3
>>> foo = '0123456'
>>> ''.join(reversed(foo[0:4]))
'3210'


省略切片符号中的结束索引:

1
2
3
>>> foo = '0123456'
>>> foo[3::-1]
'3210'

如果必须多次执行此操作,请创建一个可反复使用的切片对象。

1
2
3
>>> i = slice(3,None,-1)
>>> foo[i]
'3210'


阅读"技术文档"(此处)-特别是以下句子:

If either bound is negative, the sequence’s length is added to it.

我决定试试这个,它奏效了:

1
2
3
4
>>> foo = '0123456'
>>> foo[3:-1-len(foo):-1]
'3210'
>>>

因此,我认为以编程方式确定"端点"的最佳答案是提供一个名为helper的函数,它清楚地表明,它的参数总是被视为正偏移,可能是special_slice()

我认为这个"特殊"案例的清晰性非常重要,因为许多常见和重要的用例都依赖于负偏移的默认行为(即向它们添加长度)。就我个人而言,我经常用一个"-1"结束点来表示:在最后一个元素之前停止。

因此,根据您的评论:

... algorithm that works somewhat like as follows: foo[i:i-4:-1], and starts with a high 'i' and walks down.

我可以这样做:

1
2
3
4
5
6
def slice_by_len(data, start, length, step=1):
    end = start + length if step > 0 else start - length
    if end < 0:
        # Fix the negative offset to get what we really want
        end -= len(data)
    return data[start:end:step]

然后对所需的每个切片调用它:

1
foo_part = slice_by_len(foo, i, 4, -1)

以上可以很容易地在"i"值上形成循环。


可以使用s[::-1]反转整个字符串。但是,如果要用固定长度反转每个子字符串,可以先提取子字符串,然后反转整个子字符串。例如,假设我们需要检查每个字符串foo的长度为3的子字符串是否是回文,我们可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
>>> foo = '0102030'
>>> for i in range(len(foo)-3):
...     if foo[i:i+3] == foo[i:i+3][::-1]:
...         print(foo[i:i+3], 'is a palindrome')
...     else:
...         print(foo[i:i+3], 'is not a palindrome')
...
010 is a palindrome
102 is not a palindrome
020 is a palindrome
203 is not a palindrome
030 is a palindrome

如果要检查子字符串是否是这样的回文:

1
2
if foo[i:i+3] == foo[i+2:i-1:-1]:
    ...

您将无法处理i0的情况,因为您实际上正在将foo[0:3]foo[2:-1:-1]进行比较,这相当于foo[2:n-1:-1]是一个空字符串。

第一个解决方案的唯一缺点是它使用了更多的内存,但这没什么大不了的。


除上述解决方案外,您还可以执行以下操作:

1
2
foo = '0123456'
foo[-4::-1]

我想如果foo要改变长度,这可能不是最好的解决方案,但是如果长度是静态的,它会起作用。


鉴于:

1
>>> foo = '0123456'

所需的字符串3210是从索引3到第0个字符:

1
2
>>> stop_idx=0
>>> start_idx=3

以下是两种通用解决方案:

  • 向前切片,然后反转:

    1
    2
    >>> foo[stop_idx:start_idx+1][::-1]
    '3210'
  • 基于此答案,在第一个元素(加上停止偏移量)之前使用负的步骤并停止1个元素:

    1
    2
    3
    4
    5
    >>> foo[start_idx:stop_idx-len(foo)-1:-1]
    '3210'

    >>> a[start_idx:stop_idx-len(a)-1:-1]
    [2, 1]
  • 比较执行时间,第一个版本更快:

    1
    2
    3
    4
    >>> timeit.timeit('foo[stop_idx:start_idx+1][::-1]', setup='foo="012345"; stop_idx=0; start_idx=3', number=10_000_000)
    1.7157553750148509
    >>> timeit.timeit('foo[start_idx:stop_idx-len(foo)-1:-1]', setup='foo="012345"; stop_idx=0; start_idx=3', number=10_000_000)
    1.9317215870250948

    1
    2
    3
    4
    5
    6
    7
    s="this is my world"
    pattern=re.findall(r'\S+',s)
    a=[]
    for i in range(len(pattern)):
        a.append((pattern[i][::-1]))
    print (a)
    print ("".join(a))