14

假设我想要 Python 中的列表中的第一个元素、第 3 到第 200 个元素以及第 201 个元素到最后一个元素,步长为 3。

一种方法是使用不同的索引和连接:

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

有没有办法只用一个索引来做到这一点old_list?我想要类似下面的东西(我知道这在语法上不起作用,因为列表索引不能是列表,并且因为 Python 不幸没有切片文字;我只是在寻找接近的东西):

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

我可以通过切换到 NumPy 数组来实现其中的一些,但我更感兴趣的是如何为原生 Python 列表做到这一点。我还可以创建一个切片器或类似的东西,并且可能是强大的手臂,它可以给我一个等效的切片对象来代表我所有想要的切片的组成。

但我正在寻找不涉及创建新类来管理切片的东西。我只想连接切片语法并将其提供给我的列表,并让列表了解这意味着分别获取切片并最终连接它们各自的结果。

4

6 回答 6

7

切片器对象(例如SliceMaker,来自您的其他问题,或np.s_)可以接受多个逗号分隔的切片;它们作为 a tupleof slices 或其他对象接收:

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

NumPy 将此用于多维数组,但您可以将其用于切片连接:

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]
于 2013-03-01T17:04:18.343 回答
2
import numpy as np
a = list(range(15, 50, 3))

# %%timeit -n 10000 -> 41.1 µs ± 1.71 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
[a[index] for index in np.r_[1:3, 5:7, 9:11]]
---
[18, 21, 30, 33, 42, 45]
import numpy as np
a = np.arange(15, 50, 3).astype(np.int32)

# %%timeit -n 10000 -> 31.9 µs ± 5.68 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
a[np.r_[1:3, 5:7, 9:11]]
---
array([18, 21, 30, 33, 42, 45], dtype=int32)
import numpy as np
a = np.arange(15, 50, 3).astype(np.int32)

# %%timeit -n 10000 -> 7.17 µs ± 1.17 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
slices = np.s_[1:3, 5:7, 9:11]
np.concatenate([a[_slice] for _slice in slices])
---
array([18, 21, 30, 33, 42, 45], dtype=int32)

似乎使用 numpy 是一种更快的方法。

将 numpy 部分添加到ecatmur的答案中。

import numpy as np
def xslice(x, slices):
    """Extract slices from array-like
    Args:
        x: array-like
        slices: slice or tuple of slice objects
    """
    if isinstance(slices, tuple):
        if isinstance(x, np.ndarray):
            return np.concatenate([x[_slice] for _slice in slices])
        else:
            return sum((x[s] if isinstance(s, slice) else [x[s]] for s in slices), [])        
    elif isinstance(slices, slice):
        return x[slices]
    else:
        return [x[slices]]
于 2020-12-31T09:44:47.857 回答
1

您最好编写自己的序列类型。

>>> 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)

为了让您开始:

>>> 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)
于 2012-12-12T14:52:18.613 回答
0

为什么不为您的目的创建自定义切片

>>> 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]
于 2012-12-12T15:32:03.703 回答
0

不确定这是否“更好”,但它有效,所以为什么不......

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

它可能很慢(尤其是与链相比),但它是基本的 Python(3.5.2 用于测试)

于 2017-03-05T06:53:14.290 回答
0

您可以扩展list以允许多个切片和索引:

class MultindexList(list):
    def __getitem__(self, key):
        if type(key) is tuple or type(key) is list:
            r = []
            for index in key:
                item = super().__getitem__(index)
                if type(index) is slice:
                    r += item
                else:
                    r.append(item)
            return r
        else:
            return super().__getitem__(key)


a = MultindexList(range(10))
print(a[1:3])             # [1, 2]
print(a[[1, 2]])          # [1, 2]
print(a[1, 1:3, 4:6])     # [1, 1, 2, 4, 5]
于 2020-01-03T22:01:37.787 回答