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

Python 2.6 / 3.0: Determining if a method is inherited

19 views
Skip to first unread message

Fuzzyman

unread,
Oct 5, 2008, 3:15:17 PM10/5/08
to
Hello all,

I may well be being dumb (it has happened before), but I'm struggling
to fix some code breakage with Python 2.6.

I have some code that looks for the '__lt__' method on a class:

if hasattr(clr, '__lt__'):

However - in Python 2.6 object has grown a default implementation of
'__lt__', so this test always returns True.

>>> class X(object): pass
...
>>> X.__lt__
<method-wrapper '__lt__' of type object at 0xa15cf0>
>>> X.__lt__ == object.__lt__
False

So how do I tell if the X.__lt__ is inherited from object? I can look
in the '__dict__' of the class - but that doesn't tell me if X
inherits '__lt__' from a base class other than object. (Looking inside
the method wrapper repr with a regex is not an acceptable answer...)

Some things I have tried:

>>> X.__lt__.__self__
<class '__main__.X'>
>>> dir(X.__lt__)
['__call__', '__class__', '__cmp__', '__delattr__', '__doc__',
'__format__', '__getattribute__', '__hash__', '__init__', '__name__',
'__new__', '__objclass__', '__reduce__', '__reduce_ex__', '__repr__',
'__self__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']
>>> X.__lt__.__func__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'method-wrapper' object has no attribute '__func__'


Hmmm... I can get this working with Python 2.6 with:

if '__lt__' in dir(cls):

The default implementation of '__lt__' doesn't appear in the dir of
classes. However this fails with Python 3 where the default
implementation *does* appear in the output of 'dir'. Any suggestions?

Michael Foord
--
http://www.ironpythoninaction.com/

MRAB

unread,
Oct 5, 2008, 8:13:28 PM10/5/08
to
Methods are objects. How do you know if two references refer to the
same object? You use "is":

X.__lt__ is object.__lt__

Aaron "Castironpi" Brady

unread,
Oct 5, 2008, 9:57:33 PM10/5/08
to
On Oct 5, 7:13 pm, MRAB <goo...@mrabarnett.plus.com> wrote:
> Fuzzyman wrote:
> > Hello all,
>
> > I may well be being dumb (it has happened before), but I'm struggling
> > to fix some code breakage with Python 2.6.
>
> > I have some code that looks for the '__lt__' method on a class:
>
> > if hasattr(clr, '__lt__'):
>
> > However - in Python 2.6 object has grown a default implementation of
> > '__lt__', so this test always returns True.
>
> > Hmmm... I can get this working with Python 2.6 with:
>
> Methods are objects. How do you know if two references refer to the
> same object? You use "is":
>
> X.__lt__ is object.__lt__

That doesn't work for me.

>>> class A( object ):
... pass
...
>>> class B( A ):
... def __lt__( self, other ):
... return self
...
>>> a= A()
>>> b= B()
>>> B.__lt__ is object.__lt__
False
>>> A.__lt__ is object.__lt__
False
>>>

Further, it's been noted before that

A().meth is not A().meth

Fuzzyman

unread,
Oct 6, 2008, 5:30:07 AM10/6/08
to

Didn't you see that even an equality test fails - so they are not the
same (that being the problem)...

They are unbound method objects - in Python 3 the unbound method has
gone away, so the problem is with Python 2.6.

Michael
--
http://www.ironpythoninaction.com/

Aaron "Castironpi" Brady

unread,
Oct 6, 2008, 2:01:31 PM10/6/08
to

Not tested extensively.

class NoLTException( Exception ): pass

class NoLT( object ):


def __lt__( self, other ):

raise NoLTException()

class A( NoLT ):
pass

class B( A ):


def __lt__( self, other ):

return self

def test_lt( obj ):
try:
obj.__lt__( None )
except NoLTException:
return False
except:
pass
return True

>>> a= A()
>>> b= B()
>>> test_lt( a )
False
>>> test_lt( b )
True
>>>

This method won't work for arbitrary classes, only ones that you
control, that inherit from 'NoLT'. The 'test_lt' function works by
trying to call '__lt__' on its argument. The parameter to it doesn't
matter because of what happens next. If '__lt__' raises a
NoLTException, you know it was inherited from NoLT. Otherwise, even
if another exception occurs, the object you know has '__lt__'.

It's a very object oriented solution. Essentially you're inheriting
all the classes that you want to fail, from a class that does.

Fuzzyman

unread,
Oct 6, 2008, 2:17:01 PM10/6/08
to
On Oct 6, 7:01 pm, "Aaron \"Castironpi\" Brady" <castiro...@gmail.com>
wrote:

But not a very good solution to the problem...

The specific problem is to determine if an arbitrary class implements
a specified comparison method. The general problem (that gives rise to
the specific problem) is to write a class decorator that can implement
all comparison methods from a class that implements only one.

See: http://code.activestate.com/recipes/576529/

Michael
--
http://www.ironpythoninaction.com/

Aaron "Castironpi" Brady

unread,
Oct 6, 2008, 2:23:28 PM10/6/08
to

Nope, I'm out of ideas, I'm afraid.

Fuzzyman

unread,
Oct 6, 2008, 2:52:40 PM10/6/08
to
On Oct 6, 7:23 pm, "Aaron \"Castironpi\" Brady" <castiro...@gmail.com>

Thankfully that page I pointed you to has the solution I came up with
- walk the method resolution order of the class checking in the
classes' '__dict__' to see what they explicitly implement.

Given that you can get hold of X.__lt__ I was surprised by how hard it
was to tell whether that was an inherited implementation or not.

Michael
--
http://www.ironpythoninaction.com

Pekka Laukkanen

unread,
Oct 6, 2008, 7:34:02 PM10/6/08
to Fuzzyman, pytho...@python.org
2008/10/5 Fuzzyman <fuzz...@gmail.com>:

> I may well be being dumb (it has happened before), but I'm struggling
> to fix some code breakage with Python 2.6.
>
> I have some code that looks for the '__lt__' method on a class:
>
> if hasattr(clr, '__lt__'):
>
> However - in Python 2.6 object has grown a default implementation of
> '__lt__', so this test always returns True.
>
>>>> class X(object): pass
> ...
>>>> X.__lt__
> <method-wrapper '__lt__' of type object at 0xa15cf0>
>>>> X.__lt__ == object.__lt__
> False
>
> So how do I tell if the X.__lt__ is inherited from object? I can look
> in the '__dict__' of the class - but that doesn't tell me if X
> inherits '__lt__' from a base class other than object. (Looking inside
> the method wrapper repr with a regex is not an acceptable answer...)

I don't have Python 2.6 available, but if __lt__ on it works similarly
as __str__ on Python 2.5, you might be able to achieve this either
with inspect.ismethod or by checking methods' im_class attribute
directly:

>>> class C(object):
... pass
...
>>> class D(object):
... def __str__(self):
... return ''
...
>>> class E(D):
... pass
...
>>> import inspect
>>> inspect.ismethod(C().__str__)
False
>>> inspect.ismethod(D().__str__)
True
>>> inspect.ismethod(E().__str__)
True
>>>
>>> C().__str__.im_class


Traceback (most recent call last):
File "<stdin>", line 1, in <module>

AttributeError: 'method-wrapper' object has no attribute 'im_class'
>>> D().__str__.im_class
<class '__main__.D'>
>>> E().__str__.im_class
<class '__main__.E'>


Cheers,
.peke

Pekka Laukkanen

unread,
Oct 6, 2008, 7:50:26 PM10/6/08
to Fuzzyman, pytho...@python.org
2008/10/7 Pekka Laukkanen <pe...@iki.fi>:
> 2008/10/5 Fuzzyman <fuzz...@gmail.com>:

>> I may well be being dumb (it has happened before), but I'm struggling
>> to fix some code breakage with Python 2.6.
>>
>> I have some code that looks for the '__lt__' method on a class:
>>
>> if hasattr(clr, '__lt__'):
>>
>> However - in Python 2.6 object has grown a default implementation of
>> '__lt__', so this test always returns True.
>>
>>>>> class X(object): pass
>> ...
>>>>> X.__lt__
>> <method-wrapper '__lt__' of type object at 0xa15cf0>
>>>>> X.__lt__ == object.__lt__
>> False
>>
>> So how do I tell if the X.__lt__ is inherited from object? I can look
>> in the '__dict__' of the class - but that doesn't tell me if X
>> inherits '__lt__' from a base class other than object. (Looking inside
>> the method wrapper repr with a regex is not an acceptable answer...)
>
> I don't have Python 2.6 available, but if __lt__ on it works similarly
> as __str__ on Python 2.5, you might be able to achieve this either
> with inspect.ismethod or by checking methods' im_class attribute
> directly:
>
>>>> class C(object):
> ... pass
> ...
>>>> class D(object):
> ... def __str__(self):
> ... return ''
> ...
>>>> class E(D):
> ... pass
> ...
>>>> import inspect
>>>> inspect.ismethod(C().__str__)
> False
>>>> inspect.ismethod(D().__str__)
> True
>>>> inspect.ismethod(E().__str__)
> True
>>>>
>>>> C().__str__.im_class
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> AttributeError: 'method-wrapper' object has no attribute 'im_class'
>>>> D().__str__.im_class
> <class '__main__.D'>
>>>> E().__str__.im_class
> <class '__main__.E'>

Ooops, didn't notice this was suggested already. One more attempt,
hopefully this is unique. =)

>>> C().__str__.__objclass__
<type 'object'>
>>> D().__str__.__objclass__


Traceback (most recent call last):
File "<stdin>", line 1, in <module>

AttributeError: 'function' object has no attribute '__objclass__'
>>> 'spam'.__str__.__objclass__
<type 'str'>

Someone who actually knows what __objclas__ does can probably comment
does this make any sense in your case.

Cheers,
.peke

0 new messages