Python has the built-in function type
:
class
type
(object)With one argument, return the type of an object. The return value is a type object and generally the same object as returned by
object.__class__
.
Python also has the special attribute __class__
:
instance.
__class__
The class to which a class instance belongs.
I used to believe that they both refer to the same object. However in the method abc.ABCMeta.__instancecheck__
there is a check if they are identical:
def __instancecheck__(cls, instance):
"""Override for isinstance(instance, cls)."""
# Inline the cache checking
subclass = instance.__class__
# […]
subtype = type(instance)
if subtype is subclass:
# […]
When is type(instance)
different from instance.__class__
?
type(instance)
and instance.__class__
may be different, even with new-style classes, as Guido van Rossum mentioned in PEP 3119:
Also,
isinstance(x, B)
is equivalent toissubclass(x.__class__, B) or issubclass(type(x), B)
. (It is possibletype(x)
andx.__class__
are not the same object, e.g. whenx
is a proxy object.)
For example, the function weakref.proxy
of the standard library creates proxy objects.
>>> import weakref
>>> class A: pass
...
>>> a = A()
>>> type(weakref.proxy(a))
<class 'weakproxy'>
>>> weakref.proxy(a).__class__
<class '__main__.A'>
>>> repr(weakref.proxy(a))
'<weakproxy at 0x10065ab30 to A at 0x1006534c0>'
Note that the implementation of the __repr__
method of the proxy object uses type(instance)
, not instance.__class__
, since the primary purpose the __repr__
method is to provide enough information to recreate the object when debugging.
type(instance)
The real class of an object
instance is stored on the instance in a __class__
slot (i.e. at a fixed offset in the instance layout). It can only be accessed through the data descriptor vars(object)['__class__']
(whose method __get__
allows attribute retrieval, whose method __set__
allows attribute assignment, and whose method __delete__
forbids attribute deletion), or equivalently through the built-in function type
(whose one-argument form allows attribute retrieval):
>>> class A: pass
...
>>> a = A()
>>> type(a)
<class '__main__.A'>
>>> vars(object)['__class__'].__get__(a)
<class '__main__.A'>
>>> class B: pass
...
>>> vars(object)['__class__'].__set__(a, B)
>>> type(a)
<class '__main__.B'>
>>> vars(object)['__class__'].__get__(a)
<class '__main__.B'>
>>> vars(object)['__class__'].__delete__(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't delete __class__ attribute
instance.__class__
If the data descriptor vars(object)['__class__']
is not overridden in an object
subclass, instance.__class__
accesses the real class of instance
through the data descriptor:
>>> class A: pass
...
>>> a = A()
>>> type(a)
<class '__main__.A'>
>>> a.__class__
<class '__main__.A'>
>>> class B: pass
...
>>> a.__class__ = B
>>> type(a)
<class '__main__.B'>
>>> a.__class__
<class '__main__.B'>
>>> del a.__class__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't delete __class__ attribute
But if the data descriptor vars(object)['__class__']
is overridden in an object
subclass, instance.__class__
does not access the real class of instance
. Moreover, if the override is not a data descriptor, it can itself be overridden in instance
:
>>> class A: __class__ = int # overrides vars(object)['__class__']
...
>>> a = A()
>>> type(a)
<class '__main__.A'>
>>> a.__class__
<class 'int'>
>>> a.__class__ = str # overrides vars(A)['__class__'] (not a data descriptor)
>>> type(a)
<class '__main__.A'>
>>> a.__class__
<class 'str'>
>>> del a.__class__
>>> type(a)
<class '__main__.A'>
>>> a.__class__
<class 'int'>
This is the case for old style objects (inheriting from nothing). Such objects do not have the __class__
property. I think they do it this way to prevent errors. Example for Python 2.7:
class A:
pass
class B(object):
pass
a = A()
b = B()
print(dir(a)) # ['__doc__', '__module__']
print(dir(b)) # ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
print(b.__class__) # <class '__main__.B'>
print(type(b)) # <class '__main__.B'>
#####################
# The intersting part
print(a.__class__) # __main__.A
print(type(a)) # <type 'instance'>
print(B.__class__) # <type 'type'>
print(type(B)) # <type 'type'>
print(type(A)) # <type 'classobj'>
#print(A.__class__) # AttributeError: class A has no attribute '__class__'
See this for further information:
Note: The given lines from cpython were changed in 2008 last time (commit), so it really seems to be a compatibility thing or they just forgot about it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With