67

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
Charles Merriam
  • 18,628
  • 6
  • 71
  • 79

3 Answers3

68

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.

Flavien
  • 6,493
  • 10
  • 40
  • 49
  • 20
    _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. – Benjamin Hodgson Aug 17 '12 at 10:32
  • 6
    Also affected by `__getattribute__`, which intercepts the request for `OBJ.__class__` but not for `type(OBJ)`. – kdb Oct 07 '17 at 20:33
  • 9
    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. – user2357112 Feb 16 '19 at 19:51
  • @BenjaminHodgson This mainly shows why magic `__` properties should NOT be override-able in the first place ... – jave.web Mar 23 '21 at 19:10
  • 1
    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) – jsbueno Dec 16 '21 at 17:54
34

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.

Alex Martelli
  • 811,175
  • 162
  • 1,198
  • 1,373
  • 4
    It's Python 3 time, which *should* we use? – Nick T Aug 22 '14 at 05:52
  • 3
    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. – Russia Must Remove Putin Nov 06 '14 at 15:58
  • @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? – kasperd Apr 10 '15 at 17:56
  • @kasperd Yes, if your code always inherits from object, you're safe using `type()`. – Russia Must Remove Putin Apr 10 '15 at 18:46
  • 3
    @Alex Martelli @AaronHall The built-in function `type(instance)` and the attribute `instance.__class__` can be different, even with [new-style classes](https://docs.python.org/2/reference/datamodel.html#new-style-and-classic-classes), as Guido mentioned in [PEP 3119](https://www.python.org/dev/peps/pep-3119/#the-abc-module-an-abc-support-framework): "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. – Maggyero Dec 13 '18 at 10:10
20

type(obj) and type.__class__ do not behave the same for old style classes:

>>> 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
S.Lott
  • 373,146
  • 78
  • 498
  • 766
Mark Roddy
  • 25,486
  • 19
  • 65
  • 71
  • 7
    Biggest irony is yairchu's comment now has the same problem since they switched the formatting.. :P –  Dec 01 '09 at 22:01
  • 15
    Wouldn't hurt to show *how* they behave differently, and maybe also *why*. Just saying *when* they behave differently sounds a lazy answer, even if correct. – MestreLion Apr 26 '12 at 09:21
  • 9
    worth to mention this is only the issue in Python 2. In Python 3 all three expressions will be True. – Bob Dec 28 '16 at 17:39