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
Asked By: Roger Pate

||

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.

Answered By: Alex Martelli

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.

Answered By: Mark Roddy

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.

Answered By: Flavien

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.

Answered By: odigity

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
Answered By: leogama
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.