Accessing non-consecutive elements of a list or st

2019-02-03 03:14发布

问题:

As far as I can tell, this is not officially not possible, but is there a "trick" to access arbitrary non-sequential elements of a list by slicing?

For example:

>>> L = range(0,101,10)
>>> L
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]

Now I want to be able to do

a,b = L[2,5]

so that a == 20 and b == 50

One way besides two statements would be something silly like:

a,b = L[2:6:3][:2]

But that doesn't scale at all to irregular intervals.

Maybe with list comprehension using the indices I want?

[L[x] for x in [2,5]]

I would love to know what is recommended for this common problem.

回答1:

Something like this?

def select(lst, *indices):
    return (lst[i] for i in indices)

Usage:

>>> def select(lst, *indices):
...     return (lst[i] for i in indices)
...
>>> L = range(0,101,10)
>>> a, b = select(L, 2, 5)
>>> a, b
(20, 50)

The way the function works is by returning a generator object which can be iterated over similarly to any kind of Python sequence.

As @justhalf noted in the comments, your call syntax can be changed by the way you define the function parameters.

def select(lst, indices):
    return (lst[i] for i in indices)

And then you could call the function with:

select(L, [2, 5])

or any list of your choice.

Update: I now recommend using operator.itemgetter instead unless you really need the lazy evaluation feature of generators. See John Y's answer.



回答2:

Probably the closest to what you are looking for is itemgetter (or look here for Python 2 docs):

>>> L = list(range(0, 101, 10))  # works in Python 2 or 3
>>> L
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
>>> from operator import itemgetter
>>> itemgetter(2, 5)(L)
(20, 50)


回答3:

If you can use numpy, you can do just that:

>>> import numpy
>>> the_list = numpy.array(range(0,101,10))
>>> the_indices = [2,5,7]
>>> the_subset = the_list[the_indices]
>>> print the_subset, type(the_subset)
[20 50 70] <type 'numpy.ndarray'>
>>> print list(the_subset)
[20, 50, 70]

numpy.array is very similar to list, just that it supports more operation, such as mathematical operations and also arbitrary index selection like we see here.



回答4:

Just for completeness, the method from the original question is pretty simple. You would want to wrap it in a function if L is a function itself, or assign the function result to a variable beforehand, so it doesn't get called repeatedly:

[L[x] for x in [2,5]]

Of course it would also work for a string...

["ABCDEF"[x] for x in [2,0,1]]
['C', 'A', 'B']


回答5:

None of the other answers will work for multidimensional object slicing. IMHO this is the most general solution (uses numpy):

numpy.ix_ allows you to select arbitrary indices in all dimensions of an array simultaneously.

e.g.:

>>> a = np.arange(10).reshape(2, 5) # create an array
>>> a
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])
>>> ixgrid = np.ix_([0, 1], [2, 4]) # create the slice-like grid
>>> ixgrid
(array([[0],
       [1]]), array([[2, 4]]))
>>> a[ixgrid]                       # use the grid to slice a
array([[2, 4],
       [7, 9]])