Reputation: 566
I always believed that in Python interpreter values of x.__class__
and type(x)
are equivalent. But if we do the following (in Python 2.7, 3.3 and also PyPy 2.0b1):
>>> import weakref
>>> x = set()
>>> y = weakref.proxy(x)
>>> x.__class__, isinstance(x, set), type(x)
(<type 'set'>, True, <type 'set'>)
>>> y.__class__, isinstance(y, set), type(y)
(<type 'set'>, True, <type 'weakproxy'>)
we'll see that y.__class__
corresponds to the wrapped type for weakref.proxy
(I suppose that weakref.proxy
just replaces the attribute for disguise). Even isinstance
identifies y
as set
.
But type
shows the "true" type -- weakproxy
. So, type
doesn't use __class__
attribute to identify an argument's type, does it? Does it use some "more reliable" source for this purpose? If so, can we access it directly?
Upvotes: 3
Views: 608
Reputation: 3393
x.__class__
and type(x)
are not equivalent.
type(x)
is rooted in typeobject.c, and will return the true type ob_type
.
/* Special case: type(x) should return x->ob_type */
While x.__class__
is just an attribute lookup. It is equivalent to object.__getattribute__(x, '__class__')
, unless the attribute lookup has been redefined.
object
's '__class__'
is a data descriptor, which is also defined in typeobject.c. Its getter returns ob_type
as well. Thus, in most cases, x.__class__
and type(x)
return the same thing.
But weakproxy
, namely _PyWeakref_ProxyType
, deliberately defined its own proxy_getattr
. That's why y.__class__
is not the same as type(y)
in your case.
In the following experiment, we can achieve the same effect.
class A(object):
pass
class C(object):
def __getattribute__(self, name):
if name == '__class__':
return A
return object.__getattribute__(self, name)
>>> c = C()
>>> c.__class__
<class '__main__.A'>
>>> type(c)
<class '__main__.C'>
Moreover, isinstance(c, A)
and isinstance(c, C)
are both true in this example. Since isinstance
would check the equality of ob_type
first.
Upvotes: 4