[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