[ZODB-Dev] RE: ExtensionClass/Persistent and __cmp__ is tricky

Tim Peters tim at zope.com
Sat Jun 28 22:36:27 EDT 2003


[Christian Reis]
> So I read the docs on BTrees and `Total Ordering' at
> http://www.zope.org/Wikis/ZODB/guide/node6.html again. One of the key
> points here (as Tim pointed out) is that __cmp__ has to be carefully
> designed:
>
>     If you want to use class instances as keys, and there's any
>     possibility that the structure may be stored in a database, it's
>     crucial that the class define a __cmp__() method, and that the
>     method is carefully implemented.
>
> I'm fine with this, so I started some verification of the __cmp__
> method on IndexedObject, our Persistent-derived class. The first,
> basic, test I did, however, bothers me because it seems to echo
> something similar to what Greg Ward suggested back in
> http://mail.zope.org/pipermail/zope-dev/2000-May/004420.html -- that
> cmp() is not completely compatible with ExtensionClass. Here's my
> (short) example:
>
>     >>> from ZODB import Persistent
>     >>> class Foo(Persistent):
>     ...    def __cmp__(self, other):
>     ...     print "Hi there"
>     ...     return cmp(self, other)
>     ...

This doesn't make much sense:  it's defining __cmp__ (indirectly) in terms
of __cmp__, so it's a recursive mess.

>     >>> f = Foo()
>     >>> f < 1

It also doesn't make much sense to compare a Foo against an integer.

>     Hi there [ repeated 21 times ]

Don't you also see 0 at the end here?

>     >>> f > 1
>     Hi there [ repeated 21 times ]

And don't you also see 0 at the end here?

>     >>> 1 < f
>     0
>     >>> 1 > f
>     1
>
> This goes against what I understand of the cmp() protocol. The PSL
> docs say:
>
>    __cmp__(self, other)
>        Called by comparison operations if rich comparison (see above)
>        is not defined. Should return a negative integer if self <
>        other, zero if self == other, a positive integer if self >
>        other. If no __cmp__() operation is defined, class instances
>        are compared by object identity (``address''). (Note: the
>        restriction that exceptions are not propagated by __cmp__()
>        has been removed in Python 1.5.)
>
> Am I understanding this correctly?

I don't know -- you haven't said what your understanding is <wink>.

> It seems you can't compare ExtensionClass with basic types; in Python
> 2.1.3 at least it appears to me that try_3way_compare doesn't want to
> call tp_compare on the second instance because it's not a PyInstance.

Possibly so.  I don't think anyone really understands the details of
ExtensionClass except for Jim (Fulton), and his understanding was fresh
years ago -- ExtensionClass isn't part of Python, it's part of Zope.

> PS: The interesting part is that it seems that comparing lists *does*
> work:

It remains unclear what "work" means to you -- honest!  I can't guess what
you *expect* any of these things to do.  I don't expect anything other than
that, e.g., 1<f deliver the same result as f>1.  It doesn't, so I suppose
there's a bug there.  I don't also assume that it's worth trying to fix
ExtensionClass, though (bang for the buck -- low (if any) benefit, probably
high cost).

>     >>> [] > f
>     Hi there [ repeated 21 times ]
>     0
>
> Which means I'm almost safe with using PersistentList, at any rate
> (which uses its List comparison).
>
> PPS: Anybody know why 21 times?

Assuming you're using Python 2.2.3, because of this #define in Python's
object.c:

#define NESTING_LIMIT 20

This gimmick is meant to enable Python to give a reasonable result when you
compare recursive (indirectly self-referential) data structures, like

    x = []
    x.append(x)
    y = []
    y.append(y)
    print x == y  # true, because the recursive objects are isomorphic

In the code at the top, this gimmick was tricked into giving an answer in
lieu of unbounded procedural recursion instead.

Are mixed-type and recursive comparisons really interesting to you in your
app?  If not, I advise dropping them and moving on to what you really want
to accomplish.  These are dark areas of Python, and perhaps by now only God
remembers how ExtensionClass works in all cases (I see that Greg never got a
reply to his msg you referenced above -- this isn't surprising, because it's
likely nobody knows the answer, and more to the point anybody who might have
thought they cared probably discovered it was much easier to redefine their
task so that an answer was no longer needed <0.3 wink>).




More information about the ZODB-Dev mailing list