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

Ian Bicking ianb at colorstudy.com
Fri Mar 24 00:31:37 CET 2006


Guido van Rossum wrote:
> On 3/23/06, Ian Bicking <ianb at colorstudy.com> wrote:
> 
>>Guido van Rossum wrote:
>>
>>>On 3/23/06, Ian Bicking <ianb at colorstudy.com> wrote:
>>>
>>>
>>>>This has been my personal experience with the iterators in SQLObject as
>>>>well.  The fact that an empty iterator is true tends to cause particular
>>>>problems in that case, though I notice iterkeys() acts properly in this
>>>>case; maybe part of the issue is that I'm actually using iterables
>>>>instead of iterators, where I can't actually test the truthfulness.
>>>
>>>This sounds like some kind of fundamental confusion -- you should
>>>never be tempted to test an iterator for its truth value.
>>
>>I'm testing if it is empty or not, which seems natural enough.  Or would
>>be, if it worked.
> 
> 
> Testing whether an iterator is empty or not is an oxymoron; the only
> legit way is to call next() and see whether it raises StopIteration.
> This is the fundamental confusion I am talking about. It is NOT
> "natural enough". It reveals a fundamental misunderstanding of the
> design of the iterator protocol.

I'm talking about a use case, not the protocol.  Where iterators are 
used, it is very common that you also want to distinguish between zero 
and some items.  The use case isn't odd or confused or oxymoronic -- 
it's very natural.  The problem is that the natural and common use case 
doesn't translate into nice Python when you use iterators.

> (There's also a design bug in 2.4 which perpetuates the confusion,
> unfortunately; see below.)
> 
> 
>>So I start out doing:
>>
>>   for item in select_results: ...
>>
>>Then I realize that the zero-item case is special (which is common), and do:
>>
>>   select_results = list(select_results)
>>   if select_results:
>>       ...
>>   else:
>>       for item in select_results:...
> 
> 
> You should write that like this:
> 
>   empty = True
>   for item in select_results:
>     empty = False
>     ...
>   if empty:
>     ...

I write this sometimes (if I don't want to use list()), and it hurts me. 
  It makes me unhappy.  Literally.

This came to mind:

   for count, item in enumerate(select_results):
       ...
   if not count:
       ...

But I realized that doesn't work at all.  Damn, that would have been nice.

>>That's not a very comfortable code transformation.  When I was just
>>first learning Python I thought this would work:
>>
>>   for item in select_results:
>>       ...
>>   else:
>>       ... stuff when there are no items ...
>>
>>But it doesn't work like that.
> 
> 
> Another fundamental confusion (about the for loop's else clause). It
> can't mean two different things. It means "if I didn't break out of
> the loop with a break statement".

I'm not proposing it be changed, just recalling my own learning process. 
  It was actually a really long time before I understood what that else: 
means, and my initial intuition was incorrect.

>>.iterkeys() does return an iterator with a useful __len__ method, so the
>>principle that iterators shouldn't be tested for truth doesn't seem right.
> 
> 
> Which iterkeys()? This is dependent on the object and on the Python
> version; Python 2.4 accidentally implemented __len__ on certain
> built-in iterators, which may explain why you are seeing this. It
> doesn't work pre-2.4 not post-2.5, at least not for dict.iterkeys().

And here I thought it was a feature ;)



-- 
Ian Bicking  /  ianb at colorstudy.com  /  http://blog.ianbicking.org


More information about the Python-3000 mailing list