如何对循环列表进行切片

How to slice a circular list

假设我有如下列表:

lst = [0,10,20,30,40,50,60,70]

我想要循环顺序从 index = 5index = 2 的 lst 元素。

lst[5:2] 产量 [] 我要lst[5:2] = [50,60,70,0,10]。有没有简单的库函数可以做到这一点?

如果第二项小于第一项,则简单地将切片分成两部分:

lst = [0,10,20,30,40,50,60,70]


def circslice(l, a, b):
    if b>=a:
        return l[a:b]
    else:
        return l[a:]+l[:b]
    
circslice(lst, 5, 2)

输出:[50, 60, 70, 0, 10]

您可以使用这样的函数:

def circular_indexing(list_, start_index, end_index) -> list:
    return [*list_[start_index:len(list_)], *list_[0:end_index]]

例如:

list1 = [0, 1, 2, 3]

def circular_indexing(list_, start_index, end_index) -> list:
    return [*list_[start_index:len(list_)], *list_[0:end_index]]

print(circular_indexing(list1, 2, 1))

输出:[2, 3, 0]

这个问题有两个 fast/easy 解决方案。

第一个也是更复杂的方法是覆盖 python list.__getitem__ 方法的默认 python 库实现,which has been referenced in other places on Whosebug.

这将允许您像往常一样引用切片,即 list[5:3],理论上,它会按照您定义的方式运行。这将是默认库的“本地扩展”。

反过来,您可以实现自己的函数,以循环方式遍历您的列表,满足您自己的标准。 一些伪代码:

def foo(left_idx, right_idx):
    if right_idx < left_idx:
        wrap index when right bound has been reached
    else:
        iterate normally

按照评论中的建议使用 deque

from collections import deque

d = deque(lst)

a,b = 5,2
d.rotate(-a)
list(d)[:len(lst)-a+b]

注意。我发现它不是很实用,因为它需要复制列表来创建双端队列,然后再复制一份到 slice

对于允许您仍然使用本机切片语法并保持静态类型兼容性的内容,您可以在序列周围使用轻型包装器 class:

from typing import Generic, Protocol, TypeVar


S = TypeVar('S', bound="ConcatSequence")


class CircularView(Generic[S]):

    def __init__(self, seq: S) -> None:
        self.seq = seq

    def __getitem__(self, s: slice) -> S:
        if s.start <= s.stop:
            return self.seq[s]
        else:
            wrap = len(self.seq) % s.step if s.step else 0
            return self.seq[s.start::s.step] + self.seq[wrap:s.stop:s.step]


lst = [0, 10, 20, 30, 40, 50, 60, 70]

print(CircularView(lst)[2:5])    # [20, 30, 40]
print(CircularView(lst)[5:2])    # [50, 60, 70, 0, 10]
print(CircularView(lst)[5:2:2])  # [50, 70, 0]
print(CircularView(lst)[5:3:2])  # [50, 70, 0, 20]
print(CircularView(lst)[4:3:3])  # [40, 70, 20]

使用可选的 protocol 静态类型

class ConcatSequence(Protocol):
    """
    A sequence that implements concatenation via '__add__'.

    This protocol is required instead of using 
    'collections.abc.Sequence' since not all sequence types
    implement '__add__' (for example, 'range').
    """

    def __add__(self, other):
        ...

    def __getitem__(self, item):
        ...

    def __len__(self):
        ...

这个方法passes type checking with mypy.