如何对循环列表进行切片
How to slice a circular list
假设我有如下列表:
lst = [0,10,20,30,40,50,60,70]
我想要循环顺序从 index = 5
到 index = 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):
...
假设我有如下列表:
lst = [0,10,20,30,40,50,60,70]
我想要循环顺序从 index = 5
到 index = 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):
...