[Python-3000] Iterators for dict keys, values, and items == annoying :)
Tim Peters
tim.peters at gmail.com
Fri Mar 24 19:51:00 CET 2006
[Jim Fulton]
> ...
> I'd be interested to hear if other people who have experience working
> with ZODB BTrees have been as annoyed as I've been.
Not since the first week ;-), no. Two things exacerbate this in ZODB:
- BTrees are a mapping type, but unlike other mapping types its keys()
(etc) methods don't return a list. This makes the result of BTree.keys()
surprising to many, just because it's different from the way other
keys() methods work.
- ZODB Buckets are also mapping types, and pretty much interchangeable
with BTrees (they support the same extended (relative to the base mapping
interface) set of methods with the same meanings), _except_ that
Bucket.keys() (etc) returns a list. Sometimes operations on BTrees even
return Buckets, so from one line of code to the next it's hard to remember
whether keys() (etc) will return a list or an iterator.
That said, it doesn't much matter, since BTrees.keys() (etc) returns a
particularly rich kind of iterator (as you know, it supports, e.g.,
__len__ and indexing, much like a list). There were only two ways I
got surprised:
- Typing, e.g.,
>>> b.keys()
at an interactive shell to see the keys, and getting back
<OOBTreeItems object at 0x00AD9C00>
- Writing, e.g.,
self.assertEqual(b.keys(), [1, 2, 3])
in a Bucket unit test, forgetting that it was in a test class that was also
(re)used to test BTrees.
"The solution" in both cases was to wrap the method result in list(),
and stop caring that this would make a "needless" copy when `b` was in
fact a Bucket.
If dict.keys() (etc) had also returned an iterator, I doubt anyone
would have been surprised by any of the above.
There's one other common surprise in Zope-land, namely that
for key in b.keys():
... possibly try to delete `key` from `b`
"doesn't work" when `b` is a BTree. The _expectation_, derived from
experience with Python dicts, is that it's bulletproof, but that's
again because dict.keys() has returned a distinct list and BTrees were
just different that way. It's a little nastier for BTrees because
they can't reliably detect a size change during iteration, so BTree
users _usually_ don't get the
>>> d = {1: 2, 3: 4}
>>> for key in d:
... del d[key]
...
Traceback (most recent call last):
File "<stdin>", line 1, in ?
RuntimeError: dictionary changed size during iteration
they're accustomed to when they try to mutate a dict that changes size
during iteration.
More information about the Python-3000
mailing list