Counterintuitive Python behavior

Petr Prikryl Answer.via.news.please.prikryl at skil.nospam.cz
Thu Apr 18 04:11:06 EDT 2002


Greg Weeks wrote in the answer to the previous post...
>
> [...]
> There should have been an operator in Python that
> says when objects are conceptually the same.

The problem here is what does it mean
*conceptually*.  For me *conceptually* means that
type([]) == type([]), but also
type([]) == type([1, 2, 3]).  In other words,
the *list* as the abstraction is the concept.

> For the moment, call it "eq".  The following would
> hold:
>
>     3 eq 3
>     not (3 eq 4)
>     a eq a # PROVIDED a HAS A VALUE
>     "foo" eq "foo"
>     not ([] eq [])
>     not ({} eq {})
>     () eq ()
>
> [...]
>
> Python almost has an "eq" operator.  "==" is like
> "eq" with two exceptions:
>
>     lists
>     dictionaries
>
> Since "==" is so close to "eq", it is natural to
> think of "==" as meaning conceptual sameness.
> And if "==" meant conceptual sameness, then lists
> would be immutable, similar to Lisp lists (in the
> absence of rplaca and rplacd).  In that case,
> .append(3) would have been implemented by
> consing, and your intuition would have been
> right.

For me, nor ``==`` nor ``is`` means *conceptual
sameness*, as said on the top of this message.

There are two distinct things when comparing the
objects: the object content and the object
identity.  When comparing two objects' content,
the content is the same if the structure and
the related element values inside the compared
objects are equal.  In other words, there is no
need for checking the objects identity in such
case; however, when the objects are identical
(i.e. one object) then the comparison can be
optimized to early return of true value.

Whith respect to this the ``==`` should compare
the content of comparable objects and the ``is``
operator should compare whether the identity of
the objects is equal.  For me, Python does
exactly this.

> PS: Interestingly enough, all the expressions
> involving "eq" above are true on my system with
> "eq" replaced by "is".  But (I believe) the
> following are implementation-dependent:
>
>     3 eq 3
>     "foo" eq "foo"
>     () eq ()
>
> And "eq" disagrees with "is" for (properly
> defined) immutable class instances.  And,
> although I didn't mention it above, 3 eq 3.0
> should probably hold.  (Which, I should add,
> means that Guido was *conceptually* on target
> with the "/" redefinition.)

The Python Reference Manual (Release 2.2.1),
section *5.9 Comparisons* says:

    The operators is and is not test for object
    identity: x is y is true if and only if x and
    y are the same object. x is not y yields the
    inverse truth value.

>From this point of view, the ``3 is 3.0`` should
never hold because they are of different types
(they never can be the same object);  but it
does not make sense to ask so either -- see below.

The ``3 == 3.0`` holds, because there is one more
step of the implicit conversion from int to float
and comparison of two float values (correct me,
if I am wrong).

The ``[]``, ``{}``, and ``()`` are in fact
constructors of new objects of the related type,
while ``3`` or ``"foo"`` are literals that may or
may not produce new object -- see below.

So, the ``[] is []`` should never hold, because
we are creating two instances of the list
abstraction.  On the other hand, the ``[] == []``
should always hold, because the content of the
two empty lists is equal.

> Furthermore, two "eq" objects should hash to the
> same value.  So *any* object could be a hash key.

It would be easier to implement this if there was
no keys() method of the dictionary, i.e. when you
used your *any object* content only to generate
the hash value and the hash value was used as the
key.  But then, there would be no back way from
the hash to the key value.  Moreover, two different
keys may produce the same hash values and the
dictionary structure has to discover that kind of
conflict and to do some more steps to make the
different keys really different.

If you think more about it, you will find that
Python requirements are natural, the key must be
immutable and even something more...  The
immutable key (like a tuple) must not contain
references to mutable objects, because the
references are automatically replaced by the
value of the object when the hash value is to be
determined (correct me if I am wrong).

Brian Quinlan wrote in the answer iside this thread ...
>
> >     >>> 3 is 3
> >     True
>
> This is an implementation accident.

... On the other hand, what meaningful result one
should expect from ``3 is 3``?  For me, ``3 == 3``
makes sense, but to ask whether ``3 is 3``
(i.e. whether the objects are identical) is
questionable. The Python Reference Manual
(Release 2.2.1) explains:

   5.2.2 Literals

   [... string, numbers, ...]

   All literals correspond to immutable data
   types, and hence the object's identity is less
   important than its value. Multiple evaluations
   of literals with the same value (either the
   same occurrence in the program text or a
   different occurrence) may obtain the same
   object or a different object with the same
   value.

See you,
  Petr

--
Petr Prikryl (prikrylp at skil dot cz)





More information about the Python-list mailing list