[Python-3000] Iterators for dict keys, values, and items == annoying :)

Jack Diederich jack at performancedrivers.com
Fri Mar 24 06:08:46 CET 2006


On Thu, Mar 23, 2006 at 05:27:48PM -0800, Guido van Rossum wrote:
> On 3/23/06, Ian Bicking <ianb at colorstudy.com> wrote:
> > Empty iterator or iterator that produced no items -- from the outside
> > it's the same use case.
> 
> I see it as an education issue. Because we have generators, the
> iterator protocol can't be extended to support a test for emptiness --
> that's just not something that generators can be expected to support.
> So yes, there's confusion, but there's no way to remove the confusion,
> no matter how many times you repeat the usability issues. If we extend
> the iterator protocol with a mandatory emptiness test API, that
> excludes generators from being considered iterators, and the API
> design question you have to ask is whether you want to support
> generators.

90% of the time I don't know or care if I am using an iterable or an
iterator.  As Martelli pointed out in this thread, for the times when
you don't care iterators are preferable on speed-and-space grounds.
Most of the other 10% requires special testing for empty regardless
of a list or iter.

import csv
import itertools as it

# list way
mycsv = list(csv.reader(open('foo.csv')))
header = mycsv[0]
rows = mycsv[1:]
for (d) in [dict(zip(header, row)) for (row) in rows]:
  # do stuff

# iter way
mycsv = csv.reader(open('foo.csv'))
header = mycsv.next()
for (d) in (dict(zip(header, row)) for (row) in mycsv):
  # do stuff

For finger typing the two are a push, I'd give the iter version a slight
edge in clarity.  Both examples raise exceptions for empty files.  Ian's
and other's seem to prefer the readability of iterables for that exceptional
case.

> If you find the 'empty' flag pattern too ugly, you can always write a
> helper class that takes an iterator and returns an object that
> represents the same iterator, but sometimes buffers one element. But
> the buffering violates the coroutine-ish properties of generators, so
> it should not be the only (or even the default) way to access
> generators.

[snip Guido's peek ahead iterator class]

My good sir, you have stopped at mere hack when a depraved hack was available.
Peek-ahead isn't necessary for this one, pep 343 is.

from __future__ import pep343_is_implemented

with iter_or_false(it) as test:
  for (val) in test:
   print val
  else:
   print "break wasn't called"

if (not test):
  print "it was empty!"

# implementation of iter_or_false (tested!)
import itertools
class iter_or_false(object):
  def __init__(self, iterable):
    self.it = iter(iterable)
    self.empty = True
  
  def __nonzero__(self):
    return not self.empty
  
  def __context__(self):
    return self

  def __exit__(self, *info):
    return False

  def __enter__(self):
    return self

  def __iter__(self):
    return self

  def next(self):
    val = self.it.next()
    self.empty = False
    return val

I agree it is much better to put the smarts in the SQL result class (grow 
a __nonzero__  method and a ob.rows iterator) than to force it into a general 
case  But if it absolutely must be tacked-on ...


-jackdied


More information about the Python-3000 mailing list