[Python-ideas] Syntax for key-value iteration over mappings

Steven D'Aprano steve at pearwood.info
Mon Jul 27 18:12:13 CEST 2015

On Tue, Jul 28, 2015 at 01:54:09AM +1000, Chris Angelico wrote:
> On Tue, Jul 28, 2015 at 1:39 AM, Steven D'Aprano <steve at pearwood.info> wrote:
> > Okay, so we start with this:
> >
> > mapping = {'key': 'value', ...}
> > for name:obj in mapping:
> >     log(name)
> >     process_some_object(obj)
> >
> >
> > Then, one day, somebody passes this as mapping:
> >
> > mapping = [('key', 'value'), ...]
> >
> > and the only hint that something has gone wrong is that your logs
> > contain 0 1 2 3 ... instead of the expected names. That will be some
> > fun debugging, I'm sure.
> Except that that transformation already wouldn't work. 

Exactly! The fact that it *doesn't work* with an explicit call to 
.items() is a good thing. You get an immediate error, the code doesn't 
silently do the wrong thing. Your suggestion silently does the wrong 

If you want to support iteration over both mappings and sequences of 
(key,value) tuples, you need to make a deliberate decision to do so. You 
might use a helper function:

for name, obj in pairwise_mapping(items): 

where pairwise_mapping contains the smarts to handle mappings and 
(key,value) tuples. And that's fine, because it is deliberate and 
explicit, not an accident of the syntax.

In effect, your suggestion makes the a:b syntax a "Do What I Mean" 
operation. It tries to gues whether you want to call expr.items() or 
enumerate(expr). Building DWIM into the language is probably not a good 

> I don't know of any iteration method that's oblivious to the
> difference between a dict and a list of pairs; can you offer (toy)
> usage examples?

You have to handle it yourself. The dict constructor, and update method, 
do that. dict.update's docstring says:

 |  update(...)
 |      D.update(E, **F) -> None.  Update D from dict/iterable E and F.
 |      If E has a .keys() method, does:     for k in E: D[k] = E[k]
 |      If E lacks .keys() method, does:     for (k, v) in E: D[k] = v
 |      In either case, this is followed by: for k in F: D[k] = F[k]

But notice that in your case, passing (key,value) doesn't give you the 
name=key, obj=value results you wanted. You get name=index, 
obj=(key,value) instead! DWIM is fine and dandy when it guesses what you 
want correctly, but when it doesn't, it silently does the wrong thing 
instead of giving you an immediate exception.


More information about the Python-ideas mailing list