Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Has comparison of instancemethods changed between python 2.5 and 2.4?

4 views
Skip to first unread message

Frank Niessink

unread,
Dec 15, 2006, 11:38:59 AM12/15/06
to pytho...@python.org
Hi,

I'm trying to get an application working with Python 2.5 that works fine
with Python 2.4. This issue occurs both on Windows XP as well as on Mac OSX.

Some context: I use the publisher/subscribe pattern. Observers can
register a callback with a central 'Publisher' like this:

def registerObserver(self, observer, eventType):
''' Register an observer for an event type. The observer is a
callback method that should expect one argument, an instance
of class Event defined above.
The eventType can be anything hashable, typically a string. '''
observerList = self.__observers.setdefault(eventType, [])
if observer not in observerList:
observerList.append(observer)


Now, with Python 2.5 (and not with Python 2.4) I have a callback that is
not being added to the list because, apparently, it compares equal to
some of the callbacks already in the list. However, the instance the two
methods belong to are different, i.e. id(callback) returns different
values for the two methods. Both callbacks are of type <type
'instancemethod'>. Further investigation shows that "observer ==
observerList[1]" is True.

Has instancemethod.__cmp__ changed between python 2.4 and 2.5?

Thanks, Frank

Thomas Heller

unread,
Dec 15, 2006, 12:10:13 PM12/15/06
to pytho...@python.org
Frank Niessink schrieb:

[...]


> Now, with Python 2.5 (and not with Python 2.4) I have a callback that is
> not being added to the list because, apparently, it compares equal to
> some of the callbacks already in the list. However, the instance the two
> methods belong to are different, i.e. id(callback) returns different
> values for the two methods. Both callbacks are of type <type
> 'instancemethod'>. Further investigation shows that "observer ==
> observerList[1]" is True.
>
> Has instancemethod.__cmp__ changed between python 2.4 and 2.5?
>

It seems so:

python -c "o = object(); print o.__str__ == o.__str__"

prints True with Python 2.5, and False with Python 2.4.

Thomas

Fredrik Lundh

unread,
Dec 15, 2006, 12:49:32 PM12/15/06
to pytho...@python.org
Thomas Heller wrote:

> It seems so:
>
> python -c "o = object(); print o.__str__ == o.__str__"
>
> prints True with Python 2.5, and False with Python 2.4.

that's not an instance method, though:

>>> o = object()
>>> type(o.__str__)
<type 'method-wrapper'>

using a real instance method, I get the same result under both versions:

>>> class foo(object):
... def bar(self):
... pass
...
>>> f = foo()
>>> type(f.bar)
<type 'instancemethod'>
>>> f.bar == f.bar
True

</F>

Fredrik Lundh

unread,
Dec 15, 2006, 12:53:35 PM12/15/06
to pytho...@python.org
Frank Niessink wrote:

> However, the instance the two methods belong to are different, i.e.
> id(callback) returns different values for the two methods.

are you using the *object* as the callback? otherwise, that sentence
doesn't make much sense; bound methods are generated on the fly, and the
identity may or may not be reused, depending on how things are garbage
collected:

>>> f.bar
<bound method foo.bar of <__main__.foo object at 0x009D1BD0>>
>>> id(f.bar)
10322248
>>> id(f.bar)
10322328
>>> id(f.bar)
10322328
>>> id(f.bar)
10322328
>>> id(f.bar), id(f.bar)
(10322328, 10322328)
>>> map(id, (f.bar, f.bar, f.bar))
[10322328, 10322248, 10322368]

</F>

Frank Niessink

unread,
Dec 15, 2006, 6:57:49 PM12/15/06
to pytho...@python.org
Fredrik Lundh:

> Frank Niessink wrote:
>
> > However, the instance the two methods belong to are different, i.e.
> > id(callback) returns different values for the two methods.
>
> are you using the *object* as the callback? otherwise, that sentence
> doesn't make much sense;

You are right, that sentence doesn't make much sense. The callbacks are
instance methods, not the objects themselves. What I meant to say is
that both the instances are different and the callbacks are different
(i.e. their id is different), but the callbacks still compare as equal.
So I have to instance methods where: id(instancemethod1) !=
id(instancemethod2) but instancemethod1 == instancemethod2. However,
your explanation below explains why the id of the instance methods may
be different.

> bound methods are generated on the fly, and the
> identity may or may not be reused, depending on how things are garbage
> collected:
>
> >>> f.bar
> <bound method foo.bar of <__main__.foo object at 0x009D1BD0>>
> >>> id(f.bar)
> 10322248
> >>> id(f.bar)
> 10322328
> >>> id(f.bar)
> 10322328
> >>> id(f.bar)
> 10322328
> >>> id(f.bar), id(f.bar)
> (10322328, 10322328)
> >>> map(id, (f.bar, f.bar, f.bar))
> [10322328, 10322248, 10322368]

OK, so that explains why the id of (two references to the same)
instancemethod(s) may differ. But I'm still confused why two
instancemethods of two different instances can compare as equal.

Thanks, Frank

Frank Niessink

unread,
Dec 16, 2006, 7:19:57 AM12/16/06
to pytho...@python.org
Frank Niessink:

> OK, so that explains why the id of (two references to the same)
> instancemethod(s) may differ. But I'm still confused why two
> instancemethods of two different instances can compare as equal.

I tried to lookup the python source code where the actual comparison
happens. I think it is in methodobject.c (I'm not familiar with the
python source so please correct me if I'm wrong), meth_compare. That
function did not change between python 2.4.4 and 2.5. Moreover, the
implementation suggests that the only way for two methods to be equal is
that their instances point to the same object and their method
definitions are the same. Am I interpreting that correctly?

static int
meth_compare(PyCFunctionObject *a, PyCFunctionObject *b)
{
if (a->m_self != b->m_self)
return (a->m_self < b->m_self) ? -1 : 1;
if (a->m_ml->ml_meth == b->m_ml->ml_meth)
return 0;
if (strcmp(a->m_ml->ml_name, b->m_ml->ml_name) < 0)
return -1;
else
return 1;
}

I'll dig some deeper in my own code, maybe I have two instances that are
somehow different with python 2.4 and equal with python 2.5 and that
register the same method as callback.

Cheers, Frank

Ziga Seilnacht

unread,
Dec 16, 2006, 10:34:22 AM12/16/06
to
Frank Niessink wrote:
> I tried to lookup the python source code where the actual comparison
> happens. I think it is in methodobject.c (I'm not familiar with the
> python source so please correct me if I'm wrong), meth_compare. That
> function did not change between python 2.4.4 and 2.5. Moreover, the
> implementation suggests that the only way for two methods to be equal is
> that their instances point to the same object and their method
> definitions are the same. Am I interpreting that correctly?

No, the comparison happens in function instancemethod_compare in file
classobject.c:
http://svn.python.org/view/python/trunk/Objects/classobject.c?view=markup

This method was changed in Python 2.5. Previously, two instancemethods
compared equal if their im_self attributes were *identical* and their
im_func attributes were equal. Now, they compare equal if their im_self
attributes are *equal* and their im_func attributes are equal. See this
change:
http://svn.python.org/view?rev=46739&view=rev

A small example:

>type cmpmethod.py

class Test(object):

def meth(self):
pass

def __eq__(self, other):
return True

>python24
Python 2.4.4 (#71, Oct 18 2006, 08:34:43) ...
>>> from cmpmethod import Test
>>> a, b = Test(), Test()
>>> a.meth == b.meth
False
>>> ^Z

>python25
Python 2.5 (r25:51908, Sep 19 2006, 09:52:17) ...
>>> from cmpmethod import Test
>>> a, b = Test(), Test()
>>> a.meth == b.meth
True
>>> ^Z

If you think this is a bug, you should report it to the bugtracker:
http://sourceforge.net/bugs/?group_id=5470

Ziga

Frank Niessink

unread,
Dec 16, 2006, 4:37:44 PM12/16/06
to pytho...@python.org
Ziga Seilnacht:

> This method was changed in Python 2.5. Previously, two instancemethods
> compared equal if their im_self attributes were *identical* and their
> im_func attributes were equal. Now, they compare equal if their im_self
> attributes are *equal* and their im_func attributes are equal.

Thanks Ziga, that explains very clearly why I get the behavior I see.

> If you think this is a bug, you should report it to the bugtracker:
> http://sourceforge.net/bugs/?group_id=5470

Well, from where I am it sure feels like a bug in python, so I've
submitted a bug report:
http://sourceforge.net/tracker/index.php?func=detail&aid=1617161&group_id=5470&atid=105470

Thanks for your help, Frank

Carl Banks

unread,
Dec 16, 2006, 9:57:49 PM12/16/06
to

In the interests of practicality beating purity, I would agree that the
2.4 behavior is better. (I don't know if it would be classified as a
bug; the behavior of methods under == isn't documented AFAICT.)
There's no question that the 2.5 behavior is purer, but I can't imagine
a single use case for it.

In the meanwhile, you could work around it by using this function
instead:

def same_method(a,b):
return a.im_class is b.im_class and a.im_func is b.im_func and
a.im_self is b.im_self

Carl Banks

0 new messages