[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