Perl's @foo[3,7,1,-1] ?

K

kj

Switching from Perl here, and having a hard time letting go...

Suppose I have an "array" foo, and that I'm interested in the 4th, 8th,
second, and last element in that array. In Perl I could write:

my @wanted = @foo[3, 7, 1, -1];

I was a bit surprised when I got this in Python:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: list indices must be integers

Granted, Perl's syntax is often obscure and hard-to-read, but in
this particular case I find it quite transparent and unproblematic,
and the fictional "pythonized" form above even more so.

The best I've been able to come up with in Python are the somewhat
Perl-like-in-its-obscurity:

or the clearer but unaccountably sesquipedalian
wanted = [foo for i in 3, 7, 1, -1]
wanted = [foo[3], foo[7], foo[7], foo[-1]]


Are these the most idiomatically pythonic forms? Or am I missing
something better?

TIA!

kynn
 
J

Jack Diederich

Switching from Perl here, and having a hard time letting go...

Suppose I have an "array" foo, and that I'm interested in the 4th, 8th,
second, and last element in that array.  In Perl I could write:

 my @wanted = @foo[3, 7, 1, -1];

I was a bit surprised when I got this in Python:
wanted = foo[3, 7, 1, -1]
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
TypeError: list indices must be integers

Granted, Perl's syntax is often obscure and hard-to-read, but in
this particular case I find it quite transparent and unproblematic,
and the fictional "pythonized" form above even more so.

The best I've been able to come up with in Python are the somewhat
Perl-like-in-its-obscurity:

or the clearer but unaccountably sesquipedalian
wanted = [foo for i in 3, 7, 1, -1]
wanted = [foo[3], foo[7], foo[7], foo[-1]]


Are these the most idiomatically pythonic forms?  Or am I missing
something better?


There is only so much room in the syntax for common cases before you
end up with ... perl (no offense intended, I'm a perl monk[1]). The
Python grammar isn't as context sensitive or irregular as the perl
grammar so mylist[1,2,3] so the "1,2,3" tuple is always interpreted
as a tuple and the square brackets always expect an int or a slice.
Not including special cases everywhere means there isn't a short way
to handle special cases but it also means human readers have to
remember fewer special cases. Perl and Python make different
tradeoffs in that respect.


-Jack

[1] http://www.perlmonks.org/?node_id=111952
 
B

Brian Quinlan

kj said:
Switching from Perl here, and having a hard time letting go...

Suppose I have an "array" foo, and that I'm interested in the 4th, 8th,
second, and last element in that array. In Perl I could write:

my @wanted = @foo[3, 7, 1, -1];

Could you explain your use case? It could be that a list isn't the
appropriate data structure.

Cheers,
Brian
 
A

Arnaud Delobelle

kj said:
Switching from Perl here, and having a hard time letting go...

Suppose I have an "array" foo, and that I'm interested in the 4th, 8th,
second, and last element in that array. In Perl I could write:

my @wanted = @foo[3, 7, 1, -1];

I was a bit surprised when I got this in Python:
wanted = foo[3, 7, 1, -1]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: list indices must be integers

Granted, Perl's syntax is often obscure and hard-to-read, but in
this particular case I find it quite transparent and unproblematic,
and the fictional "pythonized" form above even more so.

The best I've been able to come up with in Python are the somewhat
Perl-like-in-its-obscurity:

or the clearer but unaccountably sesquipedalian
wanted = [foo for i in 3, 7, 1, -1]
wanted = [foo[3], foo[7], foo[7], foo[-1]]


Are these the most idiomatically pythonic forms? Or am I missing
something better?


You're missing operator.itemgetter:
 
K

kj

In said:
There is only so much room in the syntax for common cases before you
end up with ... perl (no offense intended, I'm a perl monk[1]). The
Python grammar isn't as context sensitive or irregular as the perl
grammar so mylist[1,2,3] so the "1,2,3" tuple is always interpreted
as a tuple and the square brackets always expect an int or a slice.
Not including special cases everywhere means there isn't a short way
to handle special cases but it also means human readers have to
remember fewer special cases. Perl and Python make different
tradeoffs in that respect.

OK, I see: if Python allowed foo[3,7,1,-1], then foo[3] would be
ambiguous: does it mean the fourth element of foo, or the tuple
consisting of this element alone? I suppose that's good enough
reason to veto this idea...

Thanks for all the responses.

kynn
 
K

kj

In said:
However I can't think of the last time I wanted to do this - array
elements having individual purposes are usually a sign that you should
be using a different data structure.

In the case I was working with, was a stand-in for the value returned
by some_match.groups(). The match comes from a standard regexp
defined elsewhere and that captures more groups than I need. (This
regexp is applied to every line of a log file.)

kj
 
J

John Yeung

You're missing operator.itemgetter:


('m', 'e', 'p', 's')

That looks to me like the best solution to the OP's specific
question. It's amazing how many cool things are tucked into the
standard library. While it's easy to miss these things, I appreciate
the effort to keep Python's core relatively small. (Not knowing about
itemgetter, I would have gone with the list comprehension.)

John
 
B

Brian Quinlan

kj said:
In the case I was working with, was a stand-in for the value returned
by some_match.groups(). The match comes from a standard regexp
defined elsewhere and that captures more groups than I need. (This
regexp is applied to every line of a log file.)

kj

The common idiom for this sort of thing is:

_, _, _, val1, _, _, _, val2, ..., val3 = some_match.groups()

Cheers,
Brian
 
M

MRAB

Brian said:
The common idiom for this sort of thing is:

_, _, _, val1, _, _, _, val2, ..., val3 = some_match.groups()
Alternatively:

val1, val2, val3 = some_match.group(4, 8, something)
 
B

Brian Quinlan

MRAB said:
Alternatively:

val1, val2, val3 = some_match.group(4, 8, something)

Actually, now that I think about it, naming the groups seems like it
would make this code a lot less brittle.

Cheers,
Brian
 
K

kj

In said:
OK, I see: if Python allowed foo[3,7,1,-1], then foo[3] would be
ambiguous: does it mean the fourth element of foo, or the tuple
consisting of this element alone? I suppose that's good enough
reason to veto this idea...


Hmmm, come to think of it, this argument is weaker than I thought
at first. Python already has cases where it has to deal with this
sort of ambiguity, and does so with a trailing comma. So the two
cases could be disambiguated: foo[3] for the single element, and
foo[3,] for the one-element tuple.

Also, the association of this idiom with Perl is a bit unfair:
tuple-index is very common in other languages, and in pure math as
well.

As I said in my original post, Perl code often has very obscure
expressions, but I would never describe tuple indexing as one of
them.

By the same token, the desing of Python does not entirely disregard
considerations of ease of writing. Who could argue that

foo[:]

is intrinsically clearer, or easier to read than

foo[0:len(foo)]

?

Please don't misunderstand me here. I don't want to critize, let
alone change, Python. I'm sure there is a good reason for why
Python doesn't support foo[3,7,1,-1], but I have not figured it
out yet. I still find it unconvincing that it would be for the
sake of keeping the code easy to read, because I don't see how
foo[3,7,1,-1] is any more confusing than foo[:].

kj
 
P

Piet van Oostrum

kj said:
k> Switching from Perl here, and having a hard time letting go...
k> Suppose I have an "array" foo, and that I'm interested in the 4th, 8th,
k> second, and last element in that array. In Perl I could write:
k> my @wanted = @foo[3, 7, 1, -1];
k> I was a bit surprised when I got this in Python:
wanted = foo[3, 7, 1, -1]
k> Traceback (most recent call last):
k> File "<stdin>", line 1, in <module>
k> TypeError: list indices must be integers
k> Granted, Perl's syntax is often obscure and hard-to-read, but in
k> this particular case I find it quite transparent and unproblematic,
k> and the fictional "pythonized" form above even more so.
k> The best I've been able to come up with in Python are the somewhat
k> Perl-like-in-its-obscurity:
k> or the clearer but unaccountably sesquipedalian
wanted = [foo for i in 3, 7, 1, -1]
wanted = [foo[3], foo[7], foo[7], foo[-1]]

k> Are these the most idiomatically pythonic forms? Or am I missing
k> something better?

Do it yourself:

class MyList(list):
def __getitem__(self, indx):
if isinstance (indx, tuple):
return [self for i in indx]
else:
return list.__getitem__(self, indx)

l = MyList((range(10)))

print l[3, 7, 1, -1]
print l[3]
print l[3:7]

# and now for something completely different

print l[3, (7, 1), -1]

duck :=)
 
V

Vito De Tullio

Jack said:
the square brackets always expect an int or a slice.

true only for lists :)

In [1]: mydict = {}

In [2]: mydict[1,2,3] = 'hi'

In [3]: print mydict[1,2,3]
------> print(mydict[1,2,3])
hi
 
L

Lie Ryan

Piet said:
k> Switching from Perl here, and having a hard time letting go...
k> Suppose I have an "array" foo, and that I'm interested in the 4th, 8th,
k> second, and last element in that array. In Perl I could write:
k> my @wanted = @foo[3, 7, 1, -1];
k> I was a bit surprised when I got this in Python:
wanted = foo[3, 7, 1, -1]
k> Traceback (most recent call last):
k> File "<stdin>", line 1, in <module>
k> TypeError: list indices must be integers
k> Granted, Perl's syntax is often obscure and hard-to-read, but in
k> this particular case I find it quite transparent and unproblematic,
k> and the fictional "pythonized" form above even more so.
k> The best I've been able to come up with in Python are the somewhat
k> Perl-like-in-its-obscurity:
k> or the clearer but unaccountably sesquipedalian
wanted = [foo for i in 3, 7, 1, -1]
wanted = [foo[3], foo[7], foo[7], foo[-1]]

k> Are these the most idiomatically pythonic forms? Or am I missing
k> something better?

Do it yourself:

class MyList(list):
def __getitem__(self, indx):
if isinstance (indx, tuple):
return [self for i in indx]
else:
return list.__getitem__(self, indx)

l = MyList((range(10)))

print l[3, 7, 1, -1]
print l[3]
print l[3:7]

# and now for something completely different

print l[3, (7, 1), -1]


even better:

print l[3, 4:10:2, 7]
 
S

Steven D'Aprano

kj said:
OK, I see: if Python allowed foo[3,7,1,-1], then foo[3] would be
ambiguous: does it mean the fourth element of foo, or the tuple
consisting of this element alone? I suppose that's good enough
reason to veto this idea...

There's nothing ambiguous about it. obj.__getitem__(x) already accepts two
different sorts of objects for x: ints and slice-objects:
range(8)[3] 3
range(8)[slice(3)] [0, 1, 2]
range(8)[slice(3, None)] [3, 4, 5, 6, 7]
range(8)[slice(3, 4)]
[3]


Allowing tuple arguments need not be ambiguous:

range(8)[3] => 3
range(8)[(3,)] => [3]
range(8)[(3,5,6)] => [3, 5, 6]


I've rarely needed to grab arbitrary items from a list in this fashion. I
think a more common case would be extracting fields from a tuple. In any
case, there are a few alternatives:


Grab them all, and ignore some of them (ugly for more than one or two
ignorable items):

_, x, _, _, y, _, _, _, z, _ = range(10) # x=1, y=4, z=9


Subclass list to allow tuple slices:
.... def __getitem__(self, obj):
.... if type(obj) is tuple:
.... return [self for i in obj]
.... else:
.... return list.__getitem__(self, obj)
....
L = MyList(range(10))
L[(1, 4, 8)]
[1, 4, 8]


Write a helper function:

def getitems(L, *indexes):
if len(indexes) == 1:
indexes = indexes[0]
return [L for i in indexes]


But I think this is an obvious enough extension to the __getitem__ protocol
that I for one would vote +1 on it being added to Python sequence objects
(lists, tuples, strings).
 
J

J. Cliff Dyer

Write a helper function:

def getitems(L, *indexes):
if len(indexes) == 1:
indexes = indexes[0]
return [L for i in indexes]


Whoops! Your example is broken:
cars = ['Ford', 'Toyota', 'Edsel']
getitems(cars, 1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>

I think you meant to apply that [0] to the created list instead.
Something like:

def getitems(L, *indexes):
new_list = [L for i in indexes]
if len(indexes) == 1:
new_list = new_list[0]
return new_list

But I'm not sure that would be the best idea anyway. Just let getitems
always return a list. That way the caller doesn't have to test the
length to figure out what to do with it. If you know you want a single
item, you can use regular old .__getitem__ (or .get) methods, or direct
indexing.

Then getitems can just be:

def getitems(L, *indexes):
return [L for i in indexes]
But I think this is an obvious enough extension to the __getitem__ protocol
that I for one would vote +1 on it being added to Python sequence objects
(lists, tuples, strings).

I'd be +0. It won't change my life, but it seems like a decent idea.

Cheers,
Cliff
 
A

Alan G Isaac

Switching from Perl here, and having a hard time letting go...

Suppose I have an "array" foo, and that I'm interested in the 4th, 8th,
second, and last element in that array. In Perl I could write:

my @wanted = @foo[3, 7, 1, -1];
a = np.arange(10)
a array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
a[[3,7,1,-1]]
array([3, 7, 1, 9])

hth,
Alan Isaac
 
S

Steven D'Aprano

Switching from Perl here, and having a hard time letting go...

Suppose I have an "array" foo, and that I'm interested in the 4th, 8th,
second, and last element in that array. In Perl I could write:

my @wanted = @foo[3, 7, 1, -1];
a = np.arange(10)
a array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
a[[3,7,1,-1]]
array([3, 7, 1, 9])

hth,
Alan Isaac


What's np.arange?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,067
Latest member
HunterTere

Latest Threads

Top