Re: Mutable objects as mapping keys

Guido.van.Rossum@cwi.nl
Mon, 29 Mar 1993 18:36:04 +0100

Tim Peters writes:

>Well, _I_ can live with that <smile>. In fact, if I think of a
>dictionary as mapping _objects_ to objects, this is the behavior I
>expect, since a.append(3) doesn't change a's identity _as an object_.
>It's only confusing if I think of a dictionary as mapping _values_ to
>objects. Both views can be useful, & I suspect the best thing to do is
>to compromise:
[Icon example deleted]
>The best approach is probably a philosophical mix: use "value equality"
>for an index with type string or number or Null, and "object equality"
>for everything else (I guess Null fits in either branch). The
>distinction here is best viewed as being between, say, "atomic objects"
>(those like numbers & strings with no internally visible structure) and
>"structured objects" (those, like lists & tuples, built out of simpler
>types). Probably best if d[2] and d[2L] and d[2.0] mapped to different
>things too (so that "value equality" is sensitive to type as well as to
>value).
>
>I think I just recast Icon's rules into Pythonese there. It's a pretty
>good way to go in practice, & I really don't know of a better one.

You seem to have a minority viewpoint here :-). I would argue that in
Python, in most cases, value semantics are more useful. The reason is
probably that value semantics abound elsewhere in Python: e.g. (x == y)
compares values, not object identities, and so does the test (x in y)
where y is a list: indeed (0 in [0.0]) is true.

This makes it unattractive to use object semantics for dictionary
keys, since it would break a useful invariant: if x in m.keys(),
m.has_key(x) is true and m[x] is uniquely defined.

In Python (I don't know about Icon) it is always possible to force
object semantics by wrapping a value in a class instance. The reverse
is not true.

Since most opaque object types in Python have object semantics anyway
(e.g. if you open the same file twice, the open file objects compare
different), the only types for which choosing object or value
semantics makes a difference are tuples, lists and dictionaries (yes,
using dictionaries as keys can make sense -- e.g. when calculating
functions over graphs represented as dictionaries).

I strongly believe that tuples should be treated as values, not as
objects. Consider a function of two arguments that is rather
expensive to calculate. This function could maintain a dictionary of
previously calculated function values and return values from there if
available, like this:

known = {}
def f(a, b):
if known.has_key((a,b)): return known[(a, b)]
x = real_f(a, b)
known[(a, b)] = x
return x

This sounds like a useful general mechanism to have. It also pleads
for identifying numerical keys which compare equal (not, of course,
taking it to the extreme that Perl does -- after all Python has a form
of strong typing), so f(1, 2) will reuse a known value for f(1.0, 2.0).

It is true that this doesn't work for your example of a library
routine that needs to be able to traverse *arbitrary* circular data
structures; but a built-in function returning the address of an object
as integer would be a good alternative (so you can still build a
dictionary keyed on object identity).

Regarding lists and dictionaries, the problem is this: if possible I
would like to support value semantics for those, but the
implementation makes this too difficult. If I choose object
semantics, programs expecting value semantics (and exploiting the
invariants explained above) break unexpectedly. Therefore I
choose (and have already implemented :-) not to support these types as
dictionary keys, so that at least the problem isn't obscured.

The whole exercise does point out a problem with value semantics for
mutable objects, but I still think Python uses a reasonable compromise
here, even if it is different from Icon's...

Thanks for reacting anyway,

--Guido van Rossum, CWI, Amsterdam <Guido.van.Rossum@cwi.nl>