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

Steven D'Aprano steve at pearwood.info
Mon Jul 27 21:17:10 CEST 2015


On Tue, Jul 28, 2015 at 02:25:00AM +1000, Chris Angelico wrote:

> What my suggestion was positing was not so much DWIM as "iterate over
> the keys and values of anything". A mapping type has a concept of keys
> and values; an indexable sequence (list, tuple, etc) uses sequential
> numbers as indices and its members as values.
.............^^^^^^^


Keys and indices are not the same thing, and Python is not Lua.

While there are certain similarities between the indices of a sequence 
and the keys of a mapping, they really don't play the same role in any 
meaningful sense. Consider:

x in mapping  # looks for a key x
x in sequence  # looks for a value x, not an index ("key") x

Consider this too:

mapping = {i:i**2 for i in range(1000)}
sequence = [i**2 for i in range(1000)]
for obj in (mapping, sequence):
   while 0 in obj.keys():
       del obj[0]
   assert 0 not in obj.keys()


The first problem is that lists don't have a keys() method. But that's 
okay, pretend that we've added one. Now your problems have only begun:

len(mapping)  # returns 1000-1
len(sequence)  # returns 0

Well that sucks.

Deleting (or inserting) a item into a sequence potentially changes the 
"keys" of all the other items. What sort of a mapping does that?

Despite the apparent analogy of key <=> index, it's remarkable hard to 
think of any practical use for such a thing. I cannot think of any time 
I have wanted to, or might want to in the future, ducktype lists as 
dicts, with the indices treated as keys.

The closest I can come up to is to support Lua-like arrays implemented 
as tables (mappings). When you create an array in Lua, it's not actually 
an array like in Python or a linked-list like in Lisp, but a (hash) 
table where the keys are automatically set to 1, 2, ... n by the 
interpreter. But that's just sugar for convenience. Lua arrays are still 
tables, they merely emulate arrays. And besides, that's the opposite: 
treating keys as indices, not indices as keys.

Apart from practical problems such as the above, there's also a 
conceptual problem. Keys of a mapping are *intrinsic* properties of the 
mapping. But indices of a sequence are *extrinsic* properties. They 
aren't actually part of the sequence. Given the list [2,4,6] the "key" 
(actually index) 0 is not part of the list in any way.

Some languages, like C and Python, treat those indices as starting from 
0. Others treat them as starting from 1. Fortran and Pascal, if I 
remember correctly, let you index arrays over any contiguous range of 
integers, including negatives:

foo = array[-20...20] of integer;

or something like that.

Conveniently the way we access keys and indices reflects this. Keys, 
being intrinsic to the mapping, is a method:

    mapping.keys()

while indices, being extrinsic, is a function which can be applied to 
any iterable, with any starting value:

    enumerate(sequence, 1)
    enumerate(mapping, -5)


[... snip proposal to treat sets {element} as {element:True} ...]


> This would create a new iteration invariant. We currently have:

Why do we need this invariant? What does it gain us to be able to say

    myset[element]

and get True back, regardless of the value of element? Why not just say:

    True

We can invent any invariants we like, but if they're not useful, why add 
cruft to the language to support something that we aren't going to use?


-- 
Steve


More information about the Python-ideas mailing list