3
\$\begingroup\$

iteritems on a dict can useful.

Occasionally iteritems is useful for a slice of a list and this effect can be (crudely) implemented as follows:

class List(list):
  def iteritems(self, slice=None):
    if slice is None: return enumerate(self)
    else: return itertools.izip(range(*slice.indices(len(self))), self[slice])

if __name__ == "__main__":
  l=List("hAnGtEn")
  print l
  print list(l.iteritems())
  print list(l.iteritems(slice(1,None,2)))

Output:

['h', 'A', 'n', 'G', 't', 'E', 'n']
[(0, 'h'), (1, 'A'), (2, 'n'), (3, 'G'), (4, 't'), (5, 'E'), (6, 'n')]
[(1, 'A'), (3, 'G'), (5, 'E')]

Is there a more "pythonic" list slicing syntax that should be used?

This:

range(slice.start,slice.stop,slice.step) 

does not handle certain special cases very well: e.g. where stop=-1, start=None or step=None. How can the example range/slice implementation be also improved?

edit:

range(slice.start,slice.stop,slice.step) 

is better handled with:

range(*slice.indices(len(self)))
\$\endgroup\$
3
\$\begingroup\$

Instead of

range(slice.start,slice.stop,slice.step) 

you could use this expression that handles the special cases too

range(len(self))[slice]

(This works with range on both Python 2 and 3, but not with Python 2 xrange even though it is mostly equivalent to Python 3 range)

\$\endgroup\$
  • 2
    \$\begingroup\$ I just figured out the "indicies" does a better job of range slicing: e.g. range(*slice.indices(len(self)) \$\endgroup\$ – NevilleDNZ Nov 27 '14 at 21:25
  • 1
    \$\begingroup\$ @NevilleDNZ Good find! That works even with xrange \$\endgroup\$ – Janne Karila Nov 28 '14 at 6:41
1
\$\begingroup\$

How about:

class IterItems(object):
  def __init__(self, master_l):
    self.master_l=master_l
    return super(IterItems,self).__init__()

  def __call__(self, *sss_l):
    if not sss_l: return enumerate(self.master_l)
    else: return self[slice(*sss_l)]

  def __getitem__(self,sss):
    if not isinstance(sss, slice):
      yield sss,self.master_l[sss]
    else:
      sss_l=sss.indices(len(self.master_l))
      for key in range(*sss_l): yield key,self.master_l[key]

class ListItems(list):
  def __init__(self, *arg_l, **arg_d):
    self.iteritems=IterItems(self)
    return super(ListItems,self).__init__(*arg_l, **arg_d)

if __name__ == "__main__":
  l=ListItems("hAnGtEn")
  print list(l.iteritems())
  print list(l.iteritems[1::2])
  for item in l.iteritems[1::2]: print item,
  print

Output:

[(0, 'h'), (1, 'A'), (2, 'n'), (3, 'G'), (4, 't'), (5, 'E'), (6, 'n')]
[(1, 'A'), (3, 'G'), (5, 'E')]
(1, 'A') (3, 'G') (5, 'E')

This new class ListItems works for an arbitrarily large list, providing a useful iterator that avoids loading the entire "list" into memory.

Or (if given it is feasible to short sub-slice the original long list) try...

>>> l="hAnGtEn"
>>> sss=slice(1,None,2)
>>> zip(l[sss],xrange(*sss.indices(len(l))))
[('A', 1), ('G', 3), ('E', 5)]

Or (using strict iteration):

>>> import itertools
>>> l="hAnGtEn"
>>> sss=slice(1,None,2)
>>> sss=sss.indices(len(l))
>>> list(itertools.izip(itertools.islice(l,*sss),xrange(*sss)))
[('A', 1), ('G', 3), ('E', 5)]
\$\endgroup\$
-1
\$\begingroup\$

The more pythonic way is to use enumerate():

>>> l = 'hAnGtEn'
>>> l
'hAnGtEn'
>>> list(enumerate(l))
[(0, 'h'), (1, 'A'), (2, 'n'), (3, 'G'), (4, 't'), (5, 'E'), (6, 'n')]
>>> list(enumerate(l))[1::2]
[(1, 'A'), (3, 'G'), (5, 'E')]
>>> 
\$\endgroup\$
  • \$\begingroup\$ The original already uses enumerate. \$\endgroup\$ – Janne Karila Nov 27 '14 at 9:56
  • \$\begingroup\$ You are right. I didn't study it fully. I was making the point that enumerate alone is sufficient rather than defining a new class. \$\endgroup\$ – Arvind Padmanabhan Nov 27 '14 at 11:15
  • \$\begingroup\$ The output is perfect, but I was hoping to avoid enumerating the entire (million item) list. (Hence the use of a selective iteritems) \$\endgroup\$ – NevilleDNZ Nov 27 '14 at 12:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.