Type of an object: ‘obj.__class__’ versus ‘type(obj)’

Steven D'Aprano steve+comp.lang.python at pearwood.info
Mon Dec 16 23:17:34 CET 2013


On Mon, 16 Dec 2013 12:51:21 +1100, Ben Finney wrote:

> Howdy all,
> 
> What is the Pythonic way to determine the type of an object? Are there
> multiple valid ways, and when should each be used?

That is an excellent question, I only wish I had an excellent answer to 
give you. Obviously great minds think alike because I was going to ask 
the same question, prompted by this comment from Nick Coghlan on the 
python-dev list:

"...type(obj).__name__ (working with the concrete type, ignoring any
proxying) or obj.__class__.__name__ (which takes proxying into 
account)..."

So there is a difference between them, but I'm not entirely sure what it 
is.


> We have ‘obj.__class__’, an attribute bound to the object's class. Or is
> it? When is that true, and when should we not rely on it?

I think you can rely on it. I don't believe you can delete the __class__ 
attribute from an instance:

py> class X(object):
...     pass
...
py> x = X()
py> x.__class__
<class '__main__.X'>
py> del x.__class__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't delete __class__ attribute


I think it is fair to consider __class__ to be part of the "object API" 
shared by all objects. I suppose it's possible to create a metaclass 
which does not expose a __class__ attribute, but I would consider that 
broken by design.

Furthermore, you can dynamically set the __class__ of an instance in 
order to dynamically change its type and therefore behaviour (although 
there are restrictions on what you can change it to, and from). Using the 
same x instance as above:

py> class Y(object):
...     pass
...
py> x.__class__ = Y
py> type(x)
<class '__main__.Y'>


This is by design, and it allows a very useful form of dynamic behaviour:

http://code.activestate.com/recipes/68429-ring-buffer/


 
> We have ‘type(obj)’, calling the constructor for the ‘type’ type in
> order to get a reference to the type of ‘obj’. Or is it? When is that
> true, and when should we not rely on it?

Are there circumstances where type(obj) and obj.__class__ return 
something different? Based on Nick's comment above, I would have to guess 
the answer must be yes, but I don't know what those circumstances are.


Aside: I'm not sure that it is useful to think of type as the constructor 
in the one-argument form. If you recall, prior to Python 2.2 `type` was a 
regular function which took one argument and returned the argument's type:

[steve at ando ~]$ python1.5
Python 1.5.2 (#1, Aug 27 2012, 09:09:18)  [GCC 4.1.2 20080704 (Red Hat 
4.1.2-52)] on linux2
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> type
<built-in function type>


The types returned were very different from the types we know and love 
since the class/type unification of version 2.2:

>>> type(42)
<type 'int'>
>>> int
<built-in function int>
>>> type(42)('2')
Traceback (innermost last):
  File "<stdin>", line 1, in ?
TypeError: call of non-function (type type)


So while it is *technically* correct that type(...) calls the type 
constructor, the one-argument form type(obj) is intended to behave as a 
function, while the three-argument form type(name, bases, namespace) is 
intended to behave as a constructor of types. But I digress.


> Are there other ways to get at the type of a Python object? What reasons
> are there to choose or avoid them?


I am not aware of any other ways.



-- 
Steven



More information about the Python-list mailing list