[Python-Dev] Semantic of isinstance

Phillip J. Eby pje at telecommunity.com
Tue Jun 27 05:38:06 CEST 2006


At 05:16 PM 6/26/2006 -0700, Martin Maly wrote:
> >>> class D(object):
>...     def getclass(self):
>...         print "D.getclass"
>...         return C
>...     __class__ = property(getclass)
>...
> >>> isinstance(D(), D)
>True
> >>> isinstance(D(), C)
>D.getclass
>True
>
>isinstance in this case returns True to both C and D test. I would expect 
>to see the __class__ property being called in both cases and get:
>
> >>> isinstance(D(), D)
>D.getclass
>False
>
>but that's not the case for some reason.

That's because isinstance checks type(D()) and finds it equal to D -- this 
shortcuts the process.



>  It seems that the __class__ is only accessed in some cases, but not 
> always, leading to what I think is a semantic inconsistency.

It's not inconsistent - isinstance() checks __class__ in *addition* to 
type() in order to allow proxying tricks like lying about your 
__class__.  It therefore returns true if either your real type *or* your 
__class__ matches, and as you can see, the real type is checked first.


>class E(object):
>     def getbases(self):
>         print "E.getbases"
>         return ()
>     __bases__ = property(getbases)
>
>class C(object):
>     def getbases(self):
>         print "C.getbases"
>         return (E,)                     # C() claims: "E is my base class"
>     __bases__ = property(getbases)
>
>class D(object):
>     def getclass(self):
>         print "D.getclass"
>         return C()                      # D() claims: "C() is my __class__"
>     __class__ = property(getclass)
>
>
>class F(object): pass
>
>
>print "Test 1"
>print isinstance(D(), E())              # testing against E() instance
>print "Test 2"
>print isinstance(D(), E)                # testing against E class
>
>The output here is:
>
>Test 1
>E.getbases
>D.getclass
>C.getbases
>False
>
>Test 2
>D.getclass
>False
>
>In the 2nd test, D.getclass is called to get the __class__ of D(), which 
>returns C() instance. At this point I would expect that C.getbases gets 
>called as __bases__ are retrieved, which would return tuple consisting of 
>E and ultimately produce True result.

As it happens, this is due to the fact that E is a type, while E() is 
not.  There's an optimization in the isinstance() machinery that simply 
checks to see if D().__class__ is a subtype of E.  That's where your 
experiment fails.

I'm not sure whether this behavior should be considered correct or not.



More information about the Python-Dev mailing list