Difference between type(obj) and obj.__class__
Asked Answered
A

6

85

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
Abuse answered 29/6, 2009 at 20:55 Comment(0)
J
21

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.

Jewish answered 29/6, 2009 at 21:4 Comment(0)
D
80

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.

Disquieting answered 17/5, 2012 at 9:50 Comment(5)
Caveat lector: this should be taken as an example of why you should avoid overriding __class__! You may cause code down the line that uses __class__ to break.Pewee
Also affected by __getattribute__, which intercepts the request for OBJ.__class__ but not for type(OBJ).Alcantar
Some code does this deliberately to lie about the type of objects, such as weakref.proxy. Some people think obj.__class__ should be preferred, because it believes the lie, while some people think type(obj) should be preferred because it ignores the lie. isinstance will return true if an object's real class or its lie class is an instance of the second argument.Avesta
@BenjaminHodgson This mainly shows why magic __ properties should NOT be override-able in the first place ...Cohbath
Important to notice here is that: assigning the __class__ attribute in an instance will effectively change its type, and class "for real" (including changing "type" and "isinstance" responses after the fact)Salyers
S
35

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.

Scad answered 29/6, 2009 at 21:2 Comment(5)
It's Python 3 time, which should we use?Impanel
If your code is following best practice as described by Alex, type() would be preferable. In Python 3, it is always following best practice, so use type() in Python 3.Eldridgeeldritch
@AaronHall Would I be correct for me to assume that using type() is also preferred over __class__ if I am writing Python 2 code where I know that it will only be called with instances of new-style classes?Ouellette
@Ouellette Yes, if your code always inherits from object, you're safe using type().Eldridgeeldritch
@Alex Martelli @AaronHall The built-in function type(instance) and the attribute instance.__class__ can be different, even with new-style classes, as Guido mentioned in PEP 3119: "Also, isinstance(x, B) is equivalent to issubclass(x.__class__, B) or issubclass(type(x), B). (It is possible type(x) and x.__class__ are not the same object, e.g. when x is a proxy object.)" and as @Flavien's answer illustrated.Rokach
J
21

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.

Jewish answered 29/6, 2009 at 21:4 Comment(0)
B
6

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
Buckra answered 14/8, 2022 at 1:42 Comment(0)
S
4

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.

Subtilize answered 5/6, 2022 at 14:24 Comment(0)
G
0

This will give you a more understanding of the differences: Summary: classA.one, self.class.one and type(obj).one are the same. self.one and obj.one are the same and they are distinct per instance.

class classA:

    one = "One"

    def __init__(self, val):
        self.__class__.one = val

    def show(self, marker):
        print("{}: self.one= {}, self.__class__.one= {}, classA.one= {}, type(self).one = {}".format(marker, self.one, self.__class__.one, classA.one, type(self).one))


one = classA('A')

one.show('One')

two = classA('B')
two.one = 'C'

two.show('Two')
print("{}".format(type(two).one))

three = classA('C')
classA.one = 'D'
three.one = 'E'
three.__class__.one = 'F'
three.show('Three')
two.show('Two')
one.show('One')
One: self.one= A, self.__class__.one= A, classA.one= A, type(self).one = A
Two: self.one= C, self.__class__.one= B, classA.one= B, type(self).one = B
B
Three: self.one= E, self.__class__.one= F, classA.one= F, type(self).one = F
Two: self.one= C, self.__class__.one= F, classA.one= F, type(self).one = F
One: self.one= F, self.__class__.one= F, classA.one= F, type(self).one = F

Glabrescent answered 16/2 at 18:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.