[Python-il] elegant non contiguous slice

1,740 views
Skip to first unread message

Avishalom Shalit

unread,
Dec 29, 2010, 5:11:01 PM12/29/10
to python-il
Is there a way to elegantly take the (e.g. ) 2nd 7th and 10th element in a list

i.e.
more elegant than
[arr[x] for x in [1,6,9] ]

along the lines of
arr[1,6,9]

not a big issue, but i am curious

-- vish
_______________________________________________
Python-il mailing list
Pyth...@hamakor.org.il
http://hamakor.org.il/cgi-bin/mailman/listinfo/python-il

Omer Zak

unread,
Dec 29, 2010, 5:19:47 PM12/29/10
to Python User Group
On Thu, 2010-12-30 at 00:11 +0200, Avishalom Shalit wrote:
> Is there a way to elegantly take the (e.g. ) 2nd 7th and 10th element in a list
>
> i.e.
> more elegant than
> [arr[x] for x in [1,6,9] ]
>
> along the lines of
> arr[1,6,9]
>
> not a big issue, but i am curious

Unlike Perl, Python is based upon the principle of having exactly one
way to do each thing.
List comprehension (the [arr[x] for x in [1,6,9] ] method) is one way to
accomplish the goal, and it is reasonably parsimonious. Therefore, it
is very unlikely to have another method of accomplishing the same.

However, if the indices themselves form an arithmetic sequence (such as
[1,5,9]), it is possible to use a slice notation: arr[1:10:4]

--- Omer


--
In a guided tour in Scotland:
'And, what is worn under the kilt?'
'Nothing.'
'Huh?'
'Nothing. It's all in perfectly good working order, thank you.'

My opinions, as expressed in this E-mail message, are mine alone.
They do not represent the official policy of any organization with which
I may be affiliated in any way.
WARNING TO SPAMMERS: at http://www.zak.co.il/spamwarning.html

Rani Hod

unread,
Dec 29, 2010, 5:55:15 PM12/29/10
to Omer Zak, Python User Group
Unlike Perl, Python is based upon the principle of having exactly one
way to do each thing.
List comprehension (the [arr[x] for x in [1,6,9] ] method) is one way to
accomplish the goal, and it is reasonably parsimonious.  Therefore, it
is very unlikely to have another method of accomplishing the same.

There is another method, but I wouldn't call it elegant.
It ought to be more efficient, though.

map(arr.__getitem__, (1,6,9))

Sagiv Malihi

unread,
Dec 30, 2010, 4:45:05 AM12/30/10
to Python User Group, avis...@gmail.com, Rani Hod, Omer Zak
In case you're actually using an array of numbers, and not a general list, you can use numpy to do it 'the matlab way':

>>> import numpy
>>> arr = numpy.array([1,2,3,4,5,6])
>>> arr
array([1, 2, 3, 4, 5, 6])
>>> arr[[0,5,-1]]
array([1, 6, 6])
 
- Sagiv

Shai Berger

unread,
Dec 30, 2010, 12:42:44 AM12/30/10
to pyth...@hamakor.org.il
On Thursday 30 December 2010, Avishalom Shalit wrote:
> Is there a way to elegantly take the (e.g. ) 2nd 7th and 10th element in a
> list
>
> i.e.
> more elegant than
> [arr[x] for x in [1,6,9] ]
>
> along the lines of
> arr[1,6,9]
>

Not by default, but if it's important to you, you can make it. One way to do
so: define a class L as a subclass of list, overriding its [] implementation:

class L(list):
def __getitem__(self, idx):
try:
i = iter(idx)
except TypeError:
return super(L, self).__getitem__(idx)
else:
return [self[ii] for ii in i]

With this definition in scope,

>>> arr = range(10)
>>> arr
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> L(arr)[1,6,9]
[1, 6, 9]

and even
>>> L(arr)[1,6,9,[2,3]]
[1, 6, 9, [2, 3]]

Most experienced Python users will frown upon this, though.

Alon Levy

unread,
Dec 30, 2010, 9:32:26 AM12/30/10
to Shai Berger, pyth...@hamakor.org.il
Why not do exactly what you propose, but without using operator
overloading? that way it isn't
magic anymore, doesn't override expected behavior of the getitem
protocol, and still provides
what's wanted with almost the same amount of chars (call it slice,
myarr=L([0,1.2.3]); myarr.slice([1,2])==myarr([1,2]))

--
Alon Levy

Alon Levy

unread,
Dec 31, 2010, 1:32:31 AM12/31/10
to Shai Berger, pyth...@hamakor.org.il
On Thu, Dec 30, 2010 at 9:59 PM, Shai Berger <sh...@platonix.com> wrote:
>
> On Thursday 30 December 2010, Alon Levy wrote:
> (edited against the top-posting)

>> On Thu, Dec 30, 2010 at 7:42 AM, Shai Berger <sh...@platonix.com> wrote:
>> >
>> > class L(list):
>> >  def __getitem__(self, idx):
>> >    try:
>> >      i = iter(idx)
>> >    except TypeError:
>> >      return super(L, self).__getitem__(idx)
>> >    else:
>> >      return [self[ii] for ii in i]
>> >
>> Why not do exactly what you propose, but without using operator
>> overloading? that way it isn't
>> magic anymore, doesn't override expected behavior of the getitem
>> protocol, and still provides
>> what's wanted with almost the same amount of chars (call it slice,
>> myarr=L([0,1.2.3]); myarr.slice([1,2])==myarr([1,2]))
>>
>
> It's a matter of priorities. I was trying to avoid the double brackets ("([])"
> or "[[]]"), which I find much more distasteful than the slight expansion of
> the getitem semantics. What I don't like about this is that it's very
> inefficient; and that holds regardless of the call syntax used.
>

Have you tried shedskin recently? it actually works. I mean, realistically, if
he actually ends up with this as a bottleneck, he could try to code it as an
iterator (no in between copy in that case), not sure if it would actually be
faster (depends really):
def slice(self, idx):
for i in idx:
yield self[i]

list(myarr.slice([1,2])) == myarr([1,2])

btw, he can of course have a constructor and slice operator that avoid
the double
parenthesis if you really want (which btw shedskin doesn't support yet ;), so:
def slice(self, *idx):
...
class myarr(list):
def __init__(self, *items):
super(list, self).__init__(items)

list(myarr.slice(1,2)) == myarr(1,2)

actually I think constructing a list and then giving it to the myarr
constructor (by inheritance) shouldn't copy the list. Actually I'm not
sure if it's any different then having a wrapper class.

> Shai.

Reply all
Reply to author
Forward
0 new messages