Python: shorter syntax for slices with gaps?

2019-04-06 17:25发布

Suppose I want the first element, the 3rd through 200th elements, and the 201st element through the last element by step-size 3, from a list in Python.

One way to do it is with distinct indexing and concatenation:

new_list = old_list[0:1] + old_list[3:201] + old_list[201::3]

Is there a way to do this with just one index on old_list? I would like something like the following (I know this doesn't syntactically work since list indices cannot be lists and since Python unfortunately doesn't have slice literals; I'm just looking for something close):

new_list = old_list[[0, 3:201, 201::3]]

I can achieve some of this by switching to NumPy arrays, but I'm more interested in how to do it for native Python lists. I could also create a slice maker or something like that, and possibly strong arm that into giving me an equivalent slice object to represent the composition of all my desired slices.

But I'm looking for something that doesn't involve creating a new class to manage the slices. I want to just sort of concatenate the slice syntax and feed that to my list and have the list understand that it means to separately get the slices and concatenate their respective results in the end.

标签: python slice
4条回答
爷的心禁止访问
2楼-- · 2019-04-06 17:59

Why don;t you create a custom slice for your purpose

>>> from itertools import chain, islice
>>> it = range(50)
>>> def cslice(iterable, *selectors):
    return chain(*(islice(iterable,*s) for s in selectors))

>>> list(cslice(it,(1,5),(10,15),(25,None,3)))
[1, 2, 3, 4, 10, 11, 12, 13, 14, 25, 28, 31, 34, 37, 40, 43, 46, 49]
查看更多
ゆ 、 Hurt°
3楼-- · 2019-04-06 18:04

A slice maker object (e.g. SliceMaker from your other question, or np.s_) can accept multiple comma-separated slices; they are received as a tuple of slices or other objects:

from numpy import s_
s_[0, 3:5, 6::3]
Out[1]: (0, slice(3, 5, None), slice(6, None, 3))

NumPy uses this for multidimensional arrays, but you can use it for slice concatenation:

def xslice(arr, slices):
    if isinstance(slices, tuple):
        return sum((arr[s] if isinstance(s, slice) else [arr[s]] for s in slices), [])
    elif isinstance(slices, slice):
        return arr[slices]
    else:
        return [arr[slices]]
xslice(list(range(10)), s_[0, 3:5, 6::3])
Out[1]: [0, 3, 4, 6, 9]
xslice(list(range(10)), s_[1])
Out[2]: [1]
xslice(list(range(10)), s_[:])
Out[3]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
查看更多
祖国的老花朵
4楼-- · 2019-04-06 18:13

You're probably better off writing your own sequence type.

>>> L = range(20)
>>> L
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> operator.itemgetter(*(range(1, 5) + range(10, 18, 3)))(L)
(1, 2, 3, 4, 10, 13, 16)

And to get you started on that:

>>> operator.itemgetter(*(range(*slice(1, 5).indices(len(L))) + range(*slice(10, 18, 3).indices(len(L)))))(L)
(1, 2, 3, 4, 10, 13, 16)
查看更多
再贱就再见
5楼-- · 2019-04-06 18:13

Not sure if this is "better", but it works so why not...

[y for x in [old_list[slice(*a)] for a in ((0,1),(3,201),(201,None,3))] for y in x]

It's probably slow (especially compared to chain) but it's basic python (3.5.2 used for testing)

查看更多
登录 后发表回答