Difference between type(obj) and obj.__class__
Question:
What is the difference between type(obj)
and obj.__class__
? Is there ever a possibility of type(obj) is not obj.__class__
?
I want to write a function that works generically on the supplied objects, using a default value of 1 in the same type as another parameter. Which variation, #1 or #2 below, is going to do the right thing?
def f(a, b=None):
if b is None:
b = type(a)(1) # #1
b = a.__class__(1) # #2
Answers:
Old-style classes are the problem, sigh:
>>> class old: pass
...
>>> x=old()
>>> type(x)
<type 'instance'>
>>> x.__class__
<class __main__.old at 0x6a150>
>>>
Not a problem in Python 3 since all classes are new-style now;-).
In Python 2, a class is new-style only if it inherits from another new-style class (including object
and the various built-in types such as dict
, list
, set
, …) or implicitly or explicitly sets __metaclass__
to type
.
type(obj)
and type.__class__
do not behave the same for old style classes in Python 2:
>>> class a(object):
... pass
...
>>> class b(a):
... pass
...
>>> class c:
... pass
...
>>> ai = a()
>>> bi = b()
>>> ci = c()
>>> type(ai) is ai.__class__
True
>>> type(bi) is bi.__class__
True
>>> type(ci) is ci.__class__
False
>>> type(ci)
<type 'instance'>
>>> ci.__class__
<class __main__.c at 0x7f437cee8600>
This is explained in the documentation:
For an old-style class, the statement x.__class__
provides the class of x, but type(x)
is always <type 'instance'>
. This reflects the fact that all old-style instances, independent of their class, are implemented with a single built-in type, called instance
.
This is an old question, but none of the answers seems to mention that. in the general case, it IS possible for a new-style class to have different values for type(instance)
and instance.__class__
:
class ClassA(object):
def display(self):
print("ClassA")
class ClassB(object):
__class__ = ClassA
def display(self):
print("ClassB")
instance = ClassB()
print(type(instance))
print(instance.__class__)
instance.display()
Output:
<class '__main__.ClassB'>
<class '__main__.ClassA'>
ClassB
The reason is that ClassB
is overriding the __class__
descriptor, however the internal type field in the object is not changed. type(instance)
reads directly from that type field, so it returns the correct value, whereas instance.__class__
refers to the new descriptor replacing the original descriptor provided by Python, which reads the internal type field. Instead of reading that internal type field, it returns a hardcoded value.
FYI – Django does this.
>>> from django.core.files.storage import default_storage
>>> type(default_storage)
django.core.files.storage.DefaultStorage
>>> default_storage.__class__
django.core.files.storage.FileSystemStorage
As someone with finite cognitive capacity who’s just trying to figure out what’s going in order to get work done… it’s frustrating.
There’s an interesting edge case with proxy objects (that use weak references):
>>> import weakref
>>> class MyClass:
... x = 42
...
>>> obj = MyClass()
>>> obj_proxy = weakref.proxy(obj)
>>> obj_proxy.x # proxies attribute lookup to the referenced object
42
>>> type(obj_proxy) # returns type of the proxy
weakproxy
>>> obj_proxy.__class__ # returns type of the referenced object
__main__.MyClass
>>> del obj # breaks the proxy's weak reference
>>> type(obj_proxy) # still works
weakproxy
>>> obj_proxy.__class__ # fails
ReferenceError: weakly-referenced object no longer exists
What is the difference between type(obj)
and obj.__class__
? Is there ever a possibility of type(obj) is not obj.__class__
?
I want to write a function that works generically on the supplied objects, using a default value of 1 in the same type as another parameter. Which variation, #1 or #2 below, is going to do the right thing?
def f(a, b=None):
if b is None:
b = type(a)(1) # #1
b = a.__class__(1) # #2
Old-style classes are the problem, sigh:
>>> class old: pass
...
>>> x=old()
>>> type(x)
<type 'instance'>
>>> x.__class__
<class __main__.old at 0x6a150>
>>>
Not a problem in Python 3 since all classes are new-style now;-).
In Python 2, a class is new-style only if it inherits from another new-style class (including object
and the various built-in types such as dict
, list
, set
, …) or implicitly or explicitly sets __metaclass__
to type
.
type(obj)
and type.__class__
do not behave the same for old style classes in Python 2:
>>> class a(object):
... pass
...
>>> class b(a):
... pass
...
>>> class c:
... pass
...
>>> ai = a()
>>> bi = b()
>>> ci = c()
>>> type(ai) is ai.__class__
True
>>> type(bi) is bi.__class__
True
>>> type(ci) is ci.__class__
False
>>> type(ci)
<type 'instance'>
>>> ci.__class__
<class __main__.c at 0x7f437cee8600>
This is explained in the documentation:
For an old-style class, the statement
x.__class__
provides the class of x, buttype(x)
is always<type 'instance'>
. This reflects the fact that all old-style instances, independent of their class, are implemented with a single built-in type, calledinstance
.
This is an old question, but none of the answers seems to mention that. in the general case, it IS possible for a new-style class to have different values for type(instance)
and instance.__class__
:
class ClassA(object):
def display(self):
print("ClassA")
class ClassB(object):
__class__ = ClassA
def display(self):
print("ClassB")
instance = ClassB()
print(type(instance))
print(instance.__class__)
instance.display()
Output:
<class '__main__.ClassB'>
<class '__main__.ClassA'>
ClassB
The reason is that ClassB
is overriding the __class__
descriptor, however the internal type field in the object is not changed. type(instance)
reads directly from that type field, so it returns the correct value, whereas instance.__class__
refers to the new descriptor replacing the original descriptor provided by Python, which reads the internal type field. Instead of reading that internal type field, it returns a hardcoded value.
FYI – Django does this.
>>> from django.core.files.storage import default_storage
>>> type(default_storage)
django.core.files.storage.DefaultStorage
>>> default_storage.__class__
django.core.files.storage.FileSystemStorage
As someone with finite cognitive capacity who’s just trying to figure out what’s going in order to get work done… it’s frustrating.
There’s an interesting edge case with proxy objects (that use weak references):
>>> import weakref
>>> class MyClass:
... x = 42
...
>>> obj = MyClass()
>>> obj_proxy = weakref.proxy(obj)
>>> obj_proxy.x # proxies attribute lookup to the referenced object
42
>>> type(obj_proxy) # returns type of the proxy
weakproxy
>>> obj_proxy.__class__ # returns type of the referenced object
__main__.MyClass
>>> del obj # breaks the proxy's weak reference
>>> type(obj_proxy) # still works
weakproxy
>>> obj_proxy.__class__ # fails
ReferenceError: weakly-referenced object no longer exists