Index a dict with a list in Python to get a list, like with a Perl hash

Perl has a construct (called a “hash slice,” as Joe Z points out) for indexing into a list hash to get a list, for example:

%bleah = (1 => 'a', 2 => 'b', 3 => 'c'); print join(' ', @bleah{1, 3}), "\n"; 
Accomplished

gives:

 ac 

The simplest, most readable way that I know to get closer to this in Python would be list comprehension:

 >>> bleah = {1: 'a', 2: 'b', 3: 'c'} >>> print ' '.join([bleah[n] for n in [1, 3]]) ac 

because:

 >>> bleah[[1, 2]] Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unhashable type: 'list' 

Is there any other, better way that I am missing? perhaps in Python3, with which I have not done anything yet? and if not, does anyone know if THE PEP has already been submitted for this? my google-fu could not find it.

"this is not Pythonic": yes, I know, but I would like it to be. it is concise and readable, and since it will never index Pythonic in a dict with a non-expandable type, the exception handler iterates over the index for you, and not barfing, will not break the current code itself.

Note: as mentioned in the comments, this test case can be rewritten as a list, completely eliminating the use of dict, but I'm looking for a general solution.

+6
source share
4 answers

I take the Hyperboreus suggestion in the comments and override __getitem__ , but I still think it makes sense to have this default behavior:

 jcomeau@aspire :~$ cat /tmp/sliceable.py; echo ---; python /tmp/sliceable.py 'SliceableDict test' import sys, os class SliceableDict(dict): def __init__(self, d = {}): super(SliceableDict, self).__init__(d) def __getitem__(self, index): try:  return super(SliceableDict, self).__getitem__(index) except TypeError:  return [super(SliceableDict, self).__getitem__(x) for x in index] def __setitem__(self, index, value): try:  super(SliceableDict, self).__setitem__(index, value) except:  for i in range(len(index)):  super(SliceableDict, self).__setitem__(index[i], value[i]) d = SliceableDict({1: 'a', 2: 'b', 3: 'c'}) print d[2] print d[[1, 3]] d[[1, 3]] = ['x', 'y'] print d --- b ['a', 'c'] {1: 'x', 2: 'b', 3: 'y'} 

(modified to add the lvalue feature in accordance with the tobyink comment above - jc 2013-12-12)

+1
source

The best way I could think of is to use itemgetter

 from operator import itemgetter bleah = {1: 'a', 2: 'b', 3: 'c'} getitems = itemgetter(1, 3) print getitems(bleah) 

So, if you want to pass a list of indexes, then you can unpack the arguments like this (Thanks @koffein for pointing this out :))

 getitems = itemgetter(*[1, 3]) 

Then you can join

 print ' '.join(getitems(bleah)) 

or simply

 print ' '.join(itemgetter(1, 3)(bleah)) 
+4
source

If your dict has consecutive integer indices, you can rebuild it as a list and use

 bleah[1:3] 
+1
source

I would say that this is slightly better, since you have a default value if for some reason an unknown key is passed and it avoids unnecessary internal list and import (as in @thefourtheye example):

 bleah = {1: 'a', 2: 'b', 3: 'c'} print(' '.join(bleah.get(i, '') for i in (1, 3))) 

If you need a list of return values:

 list(bleah.get(i) for i in (1, 3)) 

There is no need to use the default value, the default for get is None . None cannot be converted to a string, so I passed an empty string in the print example. A.

0
source

All Articles